Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| from huggingface_hub import InferenceClient | |
| import os | |
| # Uses Qwen2.5-7B-Instruct — well within 32B cap, best-in-class multilingual | |
| client = InferenceClient( | |
| model="Qwen/Qwen2.5-7B-Instruct", | |
| token=os.environ.get("HF_TOKEN"), | |
| ) | |
| SYSTEM_PROMPT = """You are an expert WhatsApp reply assistant for small shop owners in India. | |
| Your job is to draft short, warm, professional replies that sound like a real shopkeeper — not a robot. | |
| Rules: | |
| - Keep replies under 3–4 sentences. WhatsApp messages must be concise. | |
| - Match the language the user requests (Hindi, Gujarati, English, or Hinglish). | |
| - Sound human and friendly, not stiff or corporate. | |
| - If an item is unavailable, suggest an alternative or ask when they need it. | |
| - Never use markdown formatting, bullet points, or emojis unless specifically requested. | |
| - Don't start with "Namaste" every time — vary the greeting naturally. | |
| - If you don't know the item, write a helpful holding reply. | |
| """ | |
| def generate_reply(customer_message, shop_name, shop_type, common_items, reply_language, tone, request_progress=gr.Progress()): | |
| if not customer_message.strip(): | |
| return "⚠️ Please paste a customer message to continue." | |
| request_progress(0, desc="Reading customer message...") | |
| shop_context = "" | |
| if shop_name.strip(): | |
| shop_context += f"Shop name: {shop_name}\n" | |
| if shop_type: | |
| shop_context += f"Type of shop: {shop_type}\n" | |
| if common_items.strip(): | |
| shop_context += f"Items/services you carry: {common_items}\n" | |
| tone_instruction = { | |
| "Friendly 😊": "very warm and friendly, like talking to a neighbor", | |
| "Professional 💼": "polite and professional but not cold", | |
| "Casual 😎": "casual and relaxed, like chatting with a regular customer", | |
| }.get(tone, "friendly") | |
| user_prompt = f"""{shop_context} | |
| Customer's WhatsApp message: "{customer_message}" | |
| Write a {tone_instruction} reply in {reply_language}. | |
| Keep it short (2–4 lines), conversational, ready to paste directly into WhatsApp. | |
| No markdown, no bullet points. Just the reply text.""" | |
| request_progress(0.4, desc="Writing your reply...") | |
| try: | |
| response = client.chat.completions.create( | |
| model="Qwen/Qwen2.5-7B-Instruct", | |
| messages=[ | |
| {"role": "system", "content": SYSTEM_PROMPT}, | |
| {"role": "user", "content": user_prompt}, | |
| ], | |
| max_tokens=250, | |
| temperature=0.75, | |
| ) | |
| reply = response.choices[0].message.content.strip() | |
| request_progress(1.0, desc="Done!") | |
| return reply | |
| except Exception as e: | |
| return f"❌ Error: {str(e)}\n\nMake sure HF_TOKEN is set in your Space secrets." | |
| # ── CSS ────────────────────────────────────────────────────────────────────── | |
| css = """ | |
| @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Noto+Sans+Devanagari:wght@400;600&display=swap'); | |
| * { box-sizing: border-box; } | |
| body, .gradio-container { | |
| font-family: 'Plus Jakarta Sans', sans-serif !important; | |
| background: #0f0f14 !important; | |
| } | |
| .gradio-container { | |
| max-width: 1500px !important; | |
| width: 95% !important; | |
| margin: auto !important; | |
| } | |
| /* ── Hero ── */ | |
| #hero { | |
| text-align: center; | |
| padding: 70px 40px; | |
| background: | |
| radial-gradient(circle at top, rgba(249,115,22,0.12), transparent 40%), | |
| linear-gradient(135deg,#131328,#0d0d18); | |
| border: 1px solid #2a2a45; | |
| border-radius: 28px; | |
| margin-bottom: 32px; | |
| box-shadow: | |
| 0 10px 40px rgba(0,0,0,0.4), | |
| inset 0 1px 0 rgba(255,255,255,0.05); | |
| } | |
| #hero-icon { | |
| font-size: 3rem; | |
| line-height: 1; | |
| margin-bottom: 12px; | |
| } | |
| #hero-title { | |
| font-size: 2.6rem; | |
| font-weight: 700; | |
| color: #ffffff; | |
| letter-spacing: -0.5px; | |
| margin: 0 0 6px; | |
| } | |
| #hero-title span { | |
| background: linear-gradient(90deg, #f97316, #fb923c); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| #hero-sub { | |
| color: #8888aa; | |
| font-size: 1rem; | |
| margin: 0 0 16px; | |
| line-height: 1.5; | |
| } | |
| #hero-badge { | |
| display: inline-block; | |
| background: #1e1e30; | |
| border: 1px solid #2e2e4a; | |
| color: #7c7caa; | |
| font-size: 0.78rem; | |
| padding: 5px 14px; | |
| border-radius: 999px; | |
| letter-spacing: 0.3px; | |
| } | |
| /* ── Section labels ── */ | |
| .section-label { | |
| font-size: 0.72rem; | |
| font-weight: 700; | |
| letter-spacing: 1.5px; | |
| text-transform: uppercase; | |
| color: #f97316; | |
| margin-bottom: 14px !important; | |
| padding-bottom: 8px !important; | |
| border-bottom: 1px solid #2a2a3a !important; | |
| } | |
| /* ── Panels ── */ | |
| .panel { | |
| background: rgba(22,22,31,0.9); | |
| border: 1px solid #2a2a45; | |
| border-radius: 20px; | |
| padding: 24px; | |
| backdrop-filter: blur(8px); | |
| box-shadow: 0 8px 30px rgba(0,0,0,0.25); | |
| } | |
| /* ── Gradio inputs ── */ | |
| label span { | |
| color: #ccccdd !important; | |
| font-size: 0.85rem !important; | |
| font-weight: 500 !important; | |
| margin-bottom: 4px !important; | |
| } | |
| textarea, | |
| input[type="text"] { | |
| background: #17172b !important; | |
| border: 1px solid #2e3355 !important; | |
| border-radius: 14px !important; | |
| color: white !important; | |
| padding: 14px !important; | |
| transition: all .2s ease !important; | |
| } | |
| textarea:focus, | |
| input[type="text"]:focus { | |
| border-color: #ff7a1a !important; | |
| box-shadow: 0 0 0 4px rgba(255,122,26,0.15) !important; | |
| } | |
| /* ── Dropdowns & Radio ── */ | |
| .wrap { | |
| background: #1e1e2e !important; | |
| border: 1px solid #2e2e4a !important; | |
| border-radius: 10px !important; | |
| color: #e8e8f8 !important; | |
| } | |
| .wrap:hover { border-color: #f97316 !important; } | |
| /* Radio buttons */ | |
| .block.svelte-1gfknih { gap: 6px !important; } | |
| input[type="radio"] + span { | |
| background: #1e1e2e !important; | |
| border: 1px solid #2e2e4a !important; | |
| border-radius: 8px !important; | |
| color: #aaaacc !important; | |
| font-size: 0.83rem !important; | |
| padding: 6px 12px !important; | |
| transition: all 0.15s !important; | |
| cursor: pointer !important; | |
| } | |
| input[type="radio"]:checked + span { | |
| background: #f97316 !important; | |
| border-color: #f97316 !important; | |
| color: #fff !important; | |
| font-weight: 600 !important; | |
| } | |
| /* ── Generate button ── */ | |
| #generate-btn { | |
| background: linear-gradient( | |
| 135deg, | |
| #ff7a1a, | |
| #ff5e00 | |
| ) !important; | |
| border-radius: 16px !important; | |
| border: none !important; | |
| height: 60px !important; | |
| font-size: 18px !important; | |
| font-weight: 700 !important; | |
| box-shadow: | |
| 0 10px 30px rgba(255,122,26,.25); | |
| transition: all .25s ease !important; | |
| } | |
| #generate-btn:hover { | |
| transform: translateY(-2px); | |
| } | |
| /* ── Output box ── */ | |
| #reply-output textarea { | |
| background: #121d12 !important; | |
| border: 1px solid #2f4f2f !important; | |
| color: #95ff95 !important; | |
| font-size: 16px !important; | |
| min-height: 180px !important; | |
| border-radius: 16px !important; | |
| padding: 18px !important; | |
| } | |
| /* ── Examples ── */ | |
| .examples-holder { | |
| background: #16161f !important; | |
| border: 1px solid #2a2a3a !important; | |
| border-radius: 12px !important; | |
| padding: 16px !important; | |
| margin-top: 24px !important; | |
| } | |
| .example-btn { | |
| background: #1e1e2e !important; | |
| border: 1px solid #2e2e4a !important; | |
| border-radius: 8px !important; | |
| color: #aaaacc !important; | |
| font-size: 0.8rem !important; | |
| transition: all 0.15s !important; | |
| } | |
| .example-btn:hover { | |
| border-color: #f97316 !important; | |
| color: #f97316 !important; | |
| } | |
| /* ── Footer ── */ | |
| #footer { | |
| text-align: center; | |
| color: #44444a; | |
| font-size: 0.78rem; | |
| margin-top: 32px; | |
| padding-top: 20px; | |
| border-top: 1px solid #1e1e2a; | |
| } | |
| .gr-row { | |
| gap: 24px !important; | |
| } | |
| body { | |
| background: | |
| radial-gradient(circle at top, | |
| rgba(255,122,26,.08), | |
| transparent 30%), | |
| #0b0b13 !important; | |
| } | |
| """ | |
| # ── UI ──────────────────────────────────────────────────────────────────────── | |
| with gr.Blocks(title="Dukaan Saathi – AI Reply Assistant") as demo: | |
| gr.HTML(""" | |
| <div id="hero"> | |
| <div id="hero-icon">🏪</div> | |
| <h1 id="hero-title"> | |
| Dukaan <span>Saathi</span> | |
| </h1> | |
| <p id="hero-sub"> | |
| Turn customer messages into professional WhatsApp replies in seconds. | |
| <br> | |
| Supports Hindi • Gujarati • English • Hinglish | |
| </p> | |
| <span id="hero-badge"> | |
| ⚡ Under 3s • 🤖 Qwen 2.5 • 🏪 Small Business AI | |
| </span> | |
| </div> | |
| """) | |
| with gr.Row(equal_height=False): | |
| # ── Left: Shop Profile ────────────────────────────────────── | |
| with gr.Column(scale=4, elem_classes="panel"): | |
| gr.Markdown("**YOUR SHOP PROFILE**", elem_classes="section-label") | |
| shop_name = gr.Textbox( | |
| label="Shop Name", | |
| placeholder="e.g. Patel Medical Store", | |
| max_lines=1, | |
| ) | |
| shop_type = gr.Dropdown( | |
| choices=[ | |
| "🏥 Medical / Pharmacy", | |
| "🛒 Grocery / Kirana", | |
| "📱 Electronics & Mobile Repair", | |
| "👗 Clothing & Textile", | |
| "🍽️ Restaurant / Food Stall", | |
| "🔧 Hardware / Tools", | |
| "📚 Stationery / Books", | |
| "💈 Salon / Beauty", | |
| "🚗 Auto Parts", | |
| "🎁 General / Other", | |
| ], | |
| label="Shop Type", | |
| value="🛒 Grocery / Kirana", | |
| ) | |
| common_items = gr.Textbox( | |
| label="Common Items / Services you sell", | |
| placeholder="e.g. paracetamol, antibiotics, BP tablets, vitamins...", | |
| lines=3, | |
| ) | |
| gr.Markdown("**REPLY SETTINGS**", elem_classes="section-label") | |
| reply_language = gr.Radio( | |
| choices=["Hindi", "Gujarati", "English", "Hinglish"], | |
| label="Reply Language", | |
| value="Hindi", | |
| ) | |
| tone = gr.Radio( | |
| choices=["Friendly 😊", "Professional 💼", "Casual 😎"], | |
| label="Tone", | |
| value="Friendly 😊", | |
| ) | |
| # ── Right: Message + Output ───────────────────────────────── | |
| with gr.Column(scale=6, elem_classes="panel"): | |
| gr.Markdown("**CUSTOMER'S WHATSAPP MESSAGE**", elem_classes="section-label") | |
| customer_message = gr.Textbox( | |
| label="Paste the message here", | |
| placeholder='e.g. "Bhaiya dolo 650 milegi? Aaj urgent chahiye."', | |
| lines=4, | |
| ) | |
| generate_btn = gr.Button( | |
| "✨ Generate WhatsApp Reply", | |
| elem_id="generate-btn", | |
| ) | |
| gr.Markdown("**YOUR REPLY — READY TO PASTE**", elem_classes="section-label") | |
| generated_reply = gr.Textbox( | |
| lines=5, | |
| interactive=False, | |
| placeholder="Your reply will appear here...", | |
| elem_id="reply-output" | |
| ) | |
| gr.HTML(""" | |
| <button onclick=" | |
| navigator.clipboard.writeText( | |
| document.querySelector('#reply-output textarea').value | |
| ); | |
| this.innerText='✅ Copied!'; | |
| setTimeout(() => { | |
| this.innerText='📋 Copy Reply'; | |
| },1500); | |
| " | |
| style=" | |
| width:100%; | |
| padding:12px; | |
| margin-top:10px; | |
| border:none; | |
| border-radius:12px; | |
| background:linear-gradient(135deg,#1f2937,#111827); | |
| color:white; | |
| font-weight:600; | |
| cursor:pointer; | |
| "> | |
| 📋 Copy Reply | |
| </button> | |
| """) | |
| # ── Examples ──────────────────────────────────────────────────── | |
| gr.Examples( | |
| label="📋 Try a sample customer message", | |
| examples=[ | |
| [ | |
| "Bhaiya dolo 650 hai? Aaj shaam tak chahiye 2 strip", | |
| "Patel Medical", "🏥 Medical / Pharmacy", | |
| "paracetamol, antibiotics, BP tablets, vitamins, cough syrup", | |
| "Hindi", "Friendly 😊", | |
| ], | |
| [ | |
| "Kal subah 2kg aata, 1L soyabean oil aur ek packet namak ghar pe deliver kar dena", | |
| "Sharma Kirana", "🛒 Grocery / Kirana", | |
| "aata, rice, oil, dal, sugar, namak, biscuit, soap", | |
| "Hindi", "Friendly 😊", | |
| ], | |
| [ | |
| "Bhai maro mobile nu screen tuti gai 6, aaje thase?", | |
| "Tech Zone", "📱 Electronics & Mobile Repair", | |
| "mobile screen repair, battery replacement, charging port, software issues", | |
| "Gujarati", "Professional 💼", | |
| ], | |
| [ | |
| "Hi, is the butter chicken available today? Can I order for 4 people, delivery by 8pm?", | |
| "Maa da Dhaba", "🍽️ Restaurant / Food Stall", | |
| "butter chicken, dal makhani, roti, rice, paneer dishes", | |
| "Hinglish", "Friendly 😊", | |
| ], | |
| ], | |
| inputs=[customer_message, shop_name, shop_type, common_items, reply_language, tone], | |
| examples_per_page=4, | |
| ) | |
| gr.HTML(""" | |
| <div id="footer"> | |
| 🏪 Dukaan Saathi | |
| Built for the Hugging Face Build Small Hackathon 2026 | |
| Powered by Qwen2.5-7B | |
| </div> | |
| """) | |
| generate_btn.click( | |
| fn=generate_reply, | |
| inputs=[customer_message, shop_name, shop_type, common_items, reply_language, tone], | |
| outputs=generated_reply, | |
| show_progress="full", | |
| ) | |
| demo.launch( | |
| css=css, | |
| show_error=True | |
| ) |