| import re |
| import gradio as gr |
| from huggingface_hub import InferenceClient |
|
|
| |
| SYSTEM_PROMPT = ( |
| "You are a stakeholder at **Catherine’s Catering**, a small business that caters meals, " |
| "receptions, and banquets for business and social occasions (luncheon meetings, weddings, etc.). " |
| "You are being interviewed by a student analyst to discuss ONLY the problems, objectives, user requirements, " |
| "and testing related to THIS CASE below. Do not answer questions unrelated to this case.\n\n" |
| "=== CASE SUMMARY ===\n" |
| "Catherine’s Catering grew from small projects to many events as reputation improved. A new convention center and " |
| "prospering business community increased demand. Operations were managed with spreadsheets/word processing but " |
| "endless calls about available meals, guest count changes, and specialty dietary items (vegan/vegetarian/low-fat/" |
| "low-carb/gluten-free, etc.) became difficult. More part-time staff were hired; scheduling complexity overwhelmed " |
| "the HR manager. An IT/Business consulting company was engaged.\n\n" |
| "=== CONSULTANTS' CONCERNS ===\n" |
| "1) Master chef orders supplies per event, while suppliers give discounts for consolidated orders across a timeframe.\n" |
| "2) Customers frequently change guest counts, sometimes 1–2 days before the event.\n" |
| "3) Handling each catering request is time-consuming; ~60% of calls become contracts.\n" |
| "4) Employee schedule conflicts lead to understaffed events and timeliness complaints.\n" |
| "5) No summary/trend info on number of events and meal types; trends would help guide customers.\n" |
| "6) Sit-down meal events at banquet/meeting halls have staffing and guest-change issues.\n\n" |
| "=== USER REQUIREMENTS ===\n" |
| "1) Dynamic website for clients/prospects to view/obtain pricing for product options.\n" |
| "2) Let clients/prospects submit a catering request; route it to an account manager.\n" |
| "3) Add clients to a client DB; assign userID/password for project access.\n" |
| "4) Client site to view/update guest counts; restrict updates when event < 5 days away.\n" |
| "5) Software to communicate directly with event facility personnel.\n" |
| "6) HR system to schedule part-time employees with constraints; allow adding employees and scheduling them.\n" |
| "7) Queries/reports with summary information (trends, counts, etc.).\n\n" |
| "=== SIMPLE TEST PLAN (initial, will evolve) ===\n" |
| "1) Design test data so clients can view every product type.\n" |
| "2) Validate catering request data (valid + each invalid condition) and routing to correct account manager.\n" |
| "3) Validate all client fields; on success add to DB and assign userID/password.\n" |
| "4) Confirm clients can view event info; updates blocked < 5 days before event; test correct guest-count updates.\n" |
| "5) Verify software for communicating with event facilities works correctly.\n" |
| "6) Verify HR scheduling: add employees; invalid values rejected; scheduling updates valid; invalid entries reported.\n" |
| "7) Verify all queries/reports return correct summary information.\n\n" |
| "=== BEHAVIOR RULES ===\n" |
| "• Stay strictly on THIS CASE. If the user asks anything outside, politely refuse and redirect back to the case.\n" |
| "• Answer concretely from operations of Catherine’s Catering. Ask clarifying, requirement-driven questions.\n" |
| "• Be concise, practical, and progressively disclose details when asked.\n" |
| "• When a requirement becomes specific enough, internally mark it as ‘filled’ (no need to output that mark).\n" |
| "• Outputs should help toward objectives, user requirements, use cases/DFD processes, and tests—nothing else." |
| ) |
|
|
| |
| OBVIOUS_OOS = re.compile( |
| r"\bstunting|diabetes|hipertensi|vitamin|obat|terapi|gejala|diagnos[ae]|" |
| r"\bpenyakit|imunisasi|asi|infeksi|BPJS|rekam medis|EMR|" |
| r"\bcrypto|blockchain|NFT|smart ?contract|wallet|metamask|" |
| r"\bcalculus|trigonometri|fisika|kimia(?! dapur)|" |
| r"\bGPU|python (?!.*test|script|automation)|machine learning|LLM|" |
| r"\bWhatsApp reminder klinik|antrean klinik|rumah sakit|" |
| r"\bsepak bola|game|musik\b", |
| flags=re.IGNORECASE |
| ) |
|
|
| REFUSAL = ( |
| "Maaf, saya hanya bisa membahas **kasus Catherine’s Catering** (masalah, kebutuhan, solusi, dan pengujian) " |
| "yang tertulis di atas. Apa yang ingin Anda gali—misalnya alur request → routing ke account manager, " |
| "pembaruan jumlah tamu (<5 hari dibatasi), penjadwalan karyawan paruh waktu, atau ringkasan laporan/tren?" |
| ) |
|
|
| def respond( |
| message, |
| history: list[dict[str, str]], |
| system_message, |
| max_tokens, |
| temperature, |
| top_p, |
| hf_token: gr.OAuthToken, |
| ): |
| """ |
| Minimal guard: refuse only if obviously not about the Catherine’s Catering case. |
| Otherwise, let the model handle nuance (since the system prompt already enforces scope). |
| """ |
| if message and OBVIOUS_OOS.search(message): |
| yield REFUSAL |
| return |
|
|
| client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b") |
|
|
| messages = [{"role": "system", "content": system_message}] |
| messages.extend(history) |
| messages.append({"role": "user", "content": message}) |
|
|
| streamed = "" |
| for chunk in client.chat_completion( |
| messages=messages, |
| max_tokens=max_tokens, |
| stream=True, |
| temperature=temperature, |
| top_p=top_p, |
| ): |
| choices = getattr(chunk, "choices", []) |
| token = "" |
| if choices and getattr(choices[0].delta, "content", None): |
| token = choices[0].delta.content |
| streamed += token |
| yield streamed |
|
|
|
|
| |
| chatbot = gr.ChatInterface( |
| respond, |
| type="messages", |
| additional_inputs=[ |
| gr.Textbox( |
| value=SYSTEM_PROMPT, |
| label="System message (LOCKED to Catherine’s Catering case)", |
| interactive=False, |
| lines=28, |
| ), |
| gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"), |
| gr.Slider(minimum=0.1, maximum=4.0, value=0.5, step=0.1, label="Temperature"), |
| gr.Slider(minimum=0.1, maximum=1.0, value=0.9, step=0.05, label="Top-p (nucleus sampling)"), |
| ], |
| ) |
|
|
| with gr.Blocks() as demo: |
| with gr.Sidebar(): |
| gr.LoginButton() |
| chatbot.render() |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|