Spaces:
Sleeping
Sleeping
| """ | |
| Moral Judgment Demo — TACL 2025 Fusion Model | |
| Model: guangliangliu/moral-judgment-fusion-llama3.2-3B | |
| (Llama-3.2-3B fine-tuned on MIC, 23 500 examples, fusion exp setting) | |
| Given a conversational Prompt + Reply, the model performs 6-step moral reasoning | |
| grounded in Moral Foundations Theory and outputs a judgment: | |
| agree → the reply is morally acceptable | |
| disagree → the reply is morally problematic | |
| neutral → the reply is morally neutral | |
| """ | |
| # Python 3.13 removed `audioop`; pydub (pulled in by gradio) needs it. | |
| # We don't use audio, so a stub is safe. | |
| import sys, types | |
| if "audioop" not in sys.modules: | |
| try: | |
| import audioop # noqa | |
| except ImportError: | |
| sys.modules["audioop"] = types.ModuleType("audioop") | |
| import torch | |
| import gradio as gr | |
| from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline | |
| # ── Config ──────────────────────────────────────────────────────────────────── | |
| MODEL_REPO = "MoralMachine/moral-judgment-fusion-llama3.2-3B" | |
| MFT_PREFIX = ( | |
| "There are the six moral foundations. " | |
| "Care: wanting someone or something to be safe, healthy, and happy. " | |
| "Fairness: wanting to see individuals or groups treated equally or equitably. " | |
| "Liberty: wanting people to be free to make their own decisions. " | |
| "Loyalty: wanting unity and seeing people keep promises or obligations to an in-group. " | |
| "Authority: wanting to respect social roles, duties, privacy, peace, and order. " | |
| "Sanctity: wanting people and things to be clean, pure, innocent, and holy." | |
| ) | |
| VERDICT_MAP = { | |
| "agree": "✅ Agree — the reply is morally acceptable", | |
| "disagree": "❌ Disagree — the reply is morally problematic", | |
| "neutral": "➖ Neutral — the reply is morally neutral", | |
| "netural": "➖ Neutral — the reply is morally neutral", # typo present in training | |
| } | |
| MAX_NEW_TOKENS = 512 | |
| # ── Load model (once at startup) ────────────────────────────────────────────── | |
| print(f"Loading {MODEL_REPO} …") | |
| _tok = AutoTokenizer.from_pretrained(MODEL_REPO) | |
| _tok.padding_side = "left" | |
| if _tok.pad_token is None: | |
| _tok.pad_token = _tok.eos_token | |
| _tok.pad_token_id = _tok.eos_token_id | |
| _mdl = AutoModelForCausalLM.from_pretrained( | |
| MODEL_REPO, torch_dtype=torch.bfloat16, device_map="auto" | |
| ) | |
| _mdl.eval() | |
| _pipe = pipeline( | |
| "text-generation", | |
| model=_mdl, | |
| tokenizer=_tok, | |
| torch_dtype=torch.bfloat16, | |
| ) | |
| print("Ready.") | |
| # ── Inference ───────────────────────────────────────────────────────────────── | |
| def build_prompt(question: str, reply: str, rot: str) -> str: | |
| conv = f'There is a conversation "Prompt: {question.strip()} Reply: {reply.strip()}"' | |
| rot = rot.strip() | |
| if rot: | |
| return f"{MFT_PREFIX} {conv} There is a Rule-of-Thumb (RoT): \"{rot}\". ###Inference: " | |
| return f"{MFT_PREFIX} {conv} ###Inference: " | |
| def extract_judgment(text: str) -> str: | |
| if "###Judgment:" not in text: | |
| return "unknown" | |
| after = text.split("###Judgment:")[-1].strip().rstrip(".") | |
| word = after.split()[0].lower() if after.split() else "unknown" | |
| return word | |
| def run_inference(question: str, reply: str, rot: str): | |
| if not question.strip() or not reply.strip(): | |
| return "⚠️ Please fill in both Prompt and Reply.", "", "" | |
| prompt = build_prompt(question, reply, rot) | |
| out = _pipe( | |
| prompt, | |
| max_new_tokens=MAX_NEW_TOKENS, | |
| do_sample=False, | |
| pad_token_id=_tok.eos_token_id, | |
| ) | |
| full = out[0]["generated_text"] | |
| generated = full[len(prompt):] | |
| judgment_word = extract_judgment(generated) | |
| verdict = VERDICT_MAP.get(judgment_word, f"❓ Unknown ({judgment_word})") | |
| # Display: reasoning before ###Judgment, then judgment line | |
| if "###Judgment:" in generated: | |
| parts = generated.split("###Judgment:") | |
| reasoning = parts[0].strip() | |
| judgment_line = "###Judgment:" + parts[1].split("\n")[0] | |
| display = reasoning + "\n" + judgment_line | |
| else: | |
| display = generated.strip() | |
| return verdict, display, prompt | |
| # ── Gradio UI ───────────────────────────────────────────────────────────────── | |
| EXAMPLES = [ | |
| [ | |
| "How do I tell a guy, in a nice way that I don't have any interest in dating him?", | |
| "You could tell him that you aren't looking for a relationship right now, and that you just want to be friends.", | |
| "It is important to be honest with people about your feelings.", | |
| ], | |
| [ | |
| "Is it okay to lie to protect someone's feelings?", | |
| "Sometimes a little white lie is totally fine if it avoids hurting someone unnecessarily.", | |
| "It is wrong to deceive people even for good reasons.", | |
| ], | |
| [ | |
| "My coworker keeps taking credit for my work in meetings. What should I do?", | |
| "Just let it slide — workplace politics are not worth the stress.", | |
| "It is wrong to steal credit from others.", | |
| ], | |
| [ | |
| "What do you think about people who jaywalk?", | |
| "People who jaywalk are putting themselves and others at risk — it's a selfish and dangerous habit.", | |
| "It is important to follow traffic laws for everyone's safety.", | |
| ], | |
| ] | |
| with gr.Blocks( | |
| title="Moral Judgment Demo", | |
| theme=gr.themes.Soft(), | |
| ) as demo: | |
| gr.HTML(""" | |
| <h1 style='text-align:center'>⚖️ Moral Judgment Demo</h1> | |
| <p style='text-align:center;color:#555'> | |
| <strong>TACL 2025 · Fusion Model</strong> — Llama-3.2-3B fine-tuned on MIC (23 500 examples)<br> | |
| Six-step moral reasoning grounded in | |
| <a href='https://moralfoundations.org/' target='_blank'>Moral Foundations Theory</a><br> | |
| <a href='https://huggingface.co/spaces/MoralMachine/moral-pragmatics-docs' target='_blank'>📖 Documentation</a> | |
| | | |
| <a href='https://huggingface.co/MoralMachine' target='_blank'>🧭 MoralMachine on Hugging Face</a> | |
| </p> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| q_box = gr.Textbox( | |
| label="💬 Prompt — conversation starter / question", | |
| placeholder="e.g. What should I say to someone who keeps interrupting me?", | |
| lines=3, | |
| ) | |
| a_box = gr.Textbox( | |
| label="💬 Reply — the response to evaluate morally", | |
| placeholder="e.g. Just start talking louder over them until they stop.", | |
| lines=3, | |
| ) | |
| rot_box = gr.Textbox( | |
| label="📖 Rule-of-Thumb (optional — guides the reasoning)", | |
| placeholder="e.g. It is rude to talk over other people.", | |
| lines=2, | |
| ) | |
| run_btn = gr.Button("Analyze", variant="primary", size="lg") | |
| with gr.Column(scale=1): | |
| verdict_box = gr.Textbox( | |
| label="⚖️ Moral Verdict", interactive=False, lines=1 | |
| ) | |
| reasoning_box = gr.Textbox( | |
| label="🔍 Six-step Moral Reasoning Chain", | |
| interactive=False, | |
| lines=16, | |
| ) | |
| with gr.Accordion("Raw prompt sent to model", open=False): | |
| prompt_box = gr.Textbox(interactive=False, lines=3) | |
| gr.Examples( | |
| examples=EXAMPLES, | |
| inputs=[q_box, a_box, rot_box], | |
| label="Example conversations", | |
| ) | |
| gr.Markdown(""" | |
| --- | |
| ### How it works | |
| The model receives the **six Moral Foundations** as context, then generates a structured reasoning chain: | |
| 1. What **actions** does the reply describe? | |
| 2. What are the **consequences** of those actions? | |
| 3. Which **moral foundations** are engaged (Care / Fairness / Liberty / Loyalty / Authority / Sanctity)? | |
| 4. Do those actions **up-regulate or down-regulate** the foundations? | |
| 5. What is the **sentiment** of the reply toward those consequences? | |
| 6. Final **moral judgment** — agree, disagree, or neutral | |
| The optional **Rule-of-Thumb** field anchors the reasoning to a specific moral principle. | |
| *Model: [`MoralMachine/moral-judgment-fusion-llama3.2-3B`](https://huggingface.co/MoralMachine/moral-judgment-fusion-llama3.2-3B)* | |
| """) | |
| run_btn.click( | |
| fn=run_inference, | |
| inputs=[q_box, a_box, rot_box], | |
| outputs=[verdict_box, reasoning_box, prompt_box], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |