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("""
Turn customer messages into professional WhatsApp replies in seconds.
Supports Hindi • Gujarati • English • Hinglish