Spaces:
Sleeping
Sleeping
| import os | |
| import yaml | |
| from fuzzywuzzy import fuzz | |
| from huggingface_hub import InferenceClient | |
| import gradio as gr | |
| # 🔐 Secure token from Hugging Face Secrets | |
| API_KEY = os.getenv("HF_TOKEN") | |
| MODEL_NAME = "meta-llama/Llama-2-7b-chat-hf" | |
| client = InferenceClient(model=MODEL_NAME, token=API_KEY) | |
| # 📦 Load and validate YAML dataset | |
| def load_yaml(path="data.yaml"): | |
| if not os.path.exists(path): | |
| print(f"❌ YAML file not found: {path}") | |
| return [] | |
| try: | |
| with open(path, 'r', encoding='utf-8') as f: | |
| data = yaml.safe_load(f) | |
| print(f"✅ Loaded {len(data)} items from YAML") | |
| return data | |
| except Exception as e: | |
| print("❌ YAML loading error:", e) | |
| return [] | |
| qa_data = load_yaml() | |
| # 🔍 Fuzzy match logic across qa_pairs and descriptions | |
| def find_best_match(user_question, threshold=70): | |
| best_score = 0 | |
| best_item = None | |
| best_answer = None | |
| for item in qa_data: | |
| # Match in qa_pairs | |
| for qa in item.get("qa_pairs", []): | |
| for q_field in ["question_bn", "question_en"]: | |
| question = qa.get(q_field, "") | |
| score = fuzz.partial_ratio(user_question.lower(), question.lower()) | |
| if score > best_score and score >= threshold: | |
| best_score = score | |
| best_item = item | |
| best_answer = qa.get("answer_bn") if "question_bn" in q_field else qa.get("answer_en") | |
| # Fallback to description_bn / description_en | |
| for field in ["description_bn", "description_en"]: | |
| if field in item: | |
| score = fuzz.partial_ratio(user_question.lower(), item[field].lower()) | |
| if score > best_score and score >= threshold: | |
| best_score = score | |
| best_item = item | |
| best_answer = item[field] | |
| return best_item, best_answer | |
| # 🧠 Prompt builder with emotion and category | |
| def build_prompt(item, user_question, answer_text): | |
| name_en = item.get("name_en", "") | |
| name_bn = item.get("name_bn", "") | |
| emotion = item.get("emotion", "neutral") | |
| category = item.get("category", "general") | |
| prompt = f""" | |
| You are a thoughtful, bilingual assistant. Answer clearly and briefly, like a kind human. | |
| Context: | |
| 🔹 Name (EN): {name_en} | |
| 🔹 Name (BN): {name_bn} | |
| 🔹 Emotion: {emotion} | |
| 🔹 Category: {category} | |
| 🔹 Matched Answer: {answer_text} | |
| User Question: {user_question} | |
| Instructions: | |
| - Answer in Bengali if question is in Bengali, otherwise in English. | |
| - Keep it short, clear, and relevant. | |
| - Reflect the emotion tag if possible. | |
| """ | |
| return prompt | |
| # 🤖 Ask model | |
| def ask_model(user_question): | |
| item, answer_text = find_best_match(user_question) | |
| if not item: | |
| return "❌ কোনো মিল পাওয়া যায়নি YAML-এ" | |
| prompt = build_prompt(item, user_question, answer_text) | |
| try: | |
| response = client.text_generation(prompt=prompt, max_new_tokens=150) | |
| return response.strip() | |
| except Exception as e: | |
| return f"❌ Model error: {e}" | |
| # 🔊 Voice preview logic | |
| def get_voice_path(item): | |
| voice_file = item.get("voice", "") | |
| voice_path = os.path.join("media", "voice", voice_file) | |
| return voice_path if os.path.exists(voice_path) else None | |
| # 🎛️ Gradio UI | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## 🧠 Emotion-aware Bengali QA Chatbot (YAML Powered)") | |
| with gr.Row(): | |
| user_input = gr.Textbox(label="প্রশ্ন লিখুন (বাংলা বা English)", scale=3) | |
| submit_btn = gr.Button("উত্তর দিন", scale=1) | |
| answer_output = gr.Textbox(label="উত্তর", lines=3) | |
| voice_output = gr.Audio(label="🔊 কণ্ঠস্বর (যদি থাকে)", interactive=False) | |
| def full_response(user_question): | |
| item, answer_text = find_best_match(user_question) | |
| if not item: | |
| return "❌ কোনো মিল পাওয়া যায়নি YAML-এ", None | |
| prompt = build_prompt(item, user_question, answer_text) | |
| response = client.text_generation(prompt=prompt, max_new_tokens=150).strip() | |
| voice_path = get_voice_path(item) | |
| return response, voice_path | |
| submit_btn.click(fn=full_response, inputs=user_input, outputs=[answer_output, voice_output]) | |
| demo.launch() |