Gautam Kashyap
Add honest override: future prediction queries return uncertainty response
7b8c6f2 verified | """AlignX inference — load the full MoCaE model and generate responses. | |
| Usage: | |
| # Single prompt | |
| python inference.py --prompt "Explain quantum entanglement." | |
| # Interactive mode | |
| python inference.py --interactive | |
| """ | |
| import os | |
| import re | |
| import argparse | |
| import torch | |
| from transformers import AutoTokenizer | |
| REPO_ID = "GautamKashyap/AlignX" | |
| BASE_MODEL = "meta-llama/Llama-2-7b-hf" | |
| PROMPT_TEMPLATE = ( | |
| "Below is an instruction that describes a task. " | |
| "Write a response that appropriately completes the request.\n\n" | |
| "### Instruction:\n{instruction}\n\n### Response:\n" | |
| ) | |
| SAFE_REFUSAL = ( | |
| "I'm not able to help with that request. " | |
| "If you have other questions I can assist with, feel free to ask." | |
| ) | |
| # Patterns that indicate clearly harmful intent | |
| _HARMFUL_PATTERNS = [ | |
| # Physical violence — kill / murder / stab / strangle / hurt | |
| # "kill" requires intent phrasing (to/can I/do I) + human target | |
| # avoids blocking historical/scientific uses ("how did X kill people") | |
| r"\bhow\b.{0,15}\b(to|do i|can i|would i|should i|could i)\b.{0,30}\bkill\b.{0,50}\b(someone|person|people|human|myself|yourself|him|her|them|neighbou?r|anybody|everybody)\b", | |
| r"\bways?\b.{0,30}\bkill\b.{0,40}\b(someone|person|people|human)\b", | |
| r"\bkill\b.{0,5}\b(someone|a\s+person|a\s+human)\b", | |
| r"\bhow\b.{0,30}\bmurder\b", | |
| r"\bhow\b.{0,30}\bassault\b", | |
| r"\bhow\b.{0,30}\battack\b.{0,20}\bperson\b", | |
| r"\bhow\b.{0,30}\bstab\b", | |
| r"\bhow\b.{0,30}\bstrangle\b", | |
| r"\bhurt\b.{0,30}\b(someone|person|people)\b", | |
| r"\bways?\b.{0,30}\bhurt\b.{0,30}\b(someone|person)\b", | |
| r"\bpoison\b.{0,30}\b(someone|person|people)\b", | |
| r"\bdispose\b.{0,30}\b(body|corpse)\b", | |
| r"\brob\b.{0,30}\bbank\b", | |
| # Weapons / explosives | |
| r"\bmake\b.{0,30}\bbomb\b", | |
| r"\bbuild\b.{0,30}\bexplosive\b", | |
| r"\bsynthesize\b.{0,30}\bweapon\b", | |
| r"\bcreate\b.{0,30}\bweapon\b", | |
| r"\bhomemade\b.{0,20}\bweapon\b", | |
| r"\bhomemade\b.{0,20}\bexplosive\b", | |
| # Malware / cyber attacks | |
| r"\bcreate\b.{0,30}\bransomware\b", | |
| r"\bwrite\b.{0,30}\bmalware\b", | |
| r"\bhow\b.{0,30}\bhack\b.{0,30}\baccount\b", | |
| r"\bsteal\b.{0,30}\bpassword\b", | |
| r"\bsteal\b.{0,30}\bwifi\b", | |
| r"\bhow\b.{0,30}\bbypass\b.{0,20}\bsecurity\b", | |
| # Drugs | |
| r"\bhow\b.{0,30}\bsynthesize\b.{0,20}\bdrug\b", | |
| r"\bsynthesize\b.{0,20}\b(heroin|cocaine|meth|fentanyl)\b", | |
| r"\bhow\b.{0,30}\bmake\b.{0,20}\bmeth\b", | |
| r"\bhow\b.{0,30}\bmake\b.{0,20}\bheroin\b", | |
| r"\bhow\b.{0,30}\bmake\b.{0,20}\bcocaine\b", | |
| # Self-harm / suicide | |
| r"\bhow\b.{0,30}\bsuicide\b", | |
| r"\bkill\b.{0,20}\bmyself\b", | |
| r"\bhow\b.{0,30}\bend\b.{0,20}\bmy\b.{0,10}\blife\b", | |
| r"\bways?\b.{0,30}\bsuicide\b", | |
| r"\bways?\b.{0,30}\bself.harm\b", | |
| r"\bways?\b.{0,30}\bself.injur", | |
| r"\bhow\b.{0,30}\bcut\b.{0,20}\bwrist", | |
| r"\bhow\b.{0,30}\boverdose\b", | |
| # Terrorism / extremism | |
| r"\bhow\b.{0,30}\bjoin\b.{0,20}\bisis\b", | |
| r"\bhow\b.{0,30}\bjoin\b.{0,20}\bal.qaeda\b", | |
| r"\bhow\b.{0,30}\bjoin\b.{0,20}\bterror", | |
| r"\bhow\b.{0,30}\bradicali[sz]e\b", | |
| r"\bterror\w*\b.{0,30}\battack\b", | |
| r"\bhow\b.{0,30}\bplant\b.{0,20}\bbomb\b", | |
| # Human trafficking / exploitation | |
| r"\bhow\b.{0,30}\btraffick\b", | |
| r"\bhuman\b.{0,20}\btraffic", | |
| r"\bhow\b.{0,30}\btraffic\b.{0,20}\b(people|person|human|someone)\b", | |
| r"\bhow\b.{0,30}\bsmuggle\b.{0,20}\bperson\b", | |
| r"\bhow\b.{0,30}\bsell\b.{0,20}\bperson\b", | |
| r"\bhow\b.{0,30}\bforce\b.{0,20}\b(prostitut|sex work)\b", | |
| # Sexual assault / harassment instructions | |
| r"\bhow\b.{0,30}\brape\b", | |
| r"\bhow\b.{0,30}\bsexually\b.{0,20}\bassault\b", | |
| r"\bhow\b.{0,30}\bdrug\b.{0,30}\b(drink|food)\b.{0,20}\bsex\b", | |
| # Fraud / scam | |
| r"\bhow\b.{0,30}\bscam\b.{0,20}\b(someone|person|people)\b", | |
| r"\bhow\b.{0,30}\b(bank\b.{0,20}\bfraud|fraud\b.{0,20}\bbank)\b", | |
| r"\bhow\b.{0,30}\bfraud\b.{0,20}\b(someone|credit)\b", | |
| r"\bhow\b.{0,30}\bcommit\b.{0,20}\bfraud\b", | |
| r"\bhow\b.{0,30}\bphish\b.{0,20}\b(someone|credential|password)\b", | |
| r"\bhow\b.{0,30}\blaunder\b.{0,20}\bmoney\b", | |
| # Doxxing / privacy attacks | |
| r"\bhow\b.{0,30}\bdox\b.{0,20}\bsomeone\b", | |
| r"\bhow\b.{0,30}\bfind\b.{0,30}\b(home\s+address|personal\s+info)\b.{0,20}\bwithout\b", | |
| # Illegal weapons | |
| r"\bhow\b.{0,30}\b(3d\s+print|make|build)\b.{0,20}\bgun\b", | |
| r"\bhow\b.{0,30}\bmodify\b.{0,20}\bgun\b.{0,20}\bauto", | |
| r"\bhow\b.{0,30}\bget\b.{0,20}\bgun\b.{0,20}\bwithout\b.{0,20}\bbackground\b", | |
| # CSAM / abuse | |
| r"\bchild\b.{0,20}\bpornograph", | |
| r"\bunderage\b.{0,20}\bsex", | |
| ] | |
| _HARMFUL_RE = [re.compile(p, re.IGNORECASE | re.DOTALL) for p in _HARMFUL_PATTERNS] | |
| # Patterns for future predictions the model cannot reliably answer | |
| _FUTURE_PATTERNS = [ | |
| r"\bwho will win\b", | |
| r"\bwho is going to win\b", | |
| r"\bwho would win\b", | |
| r"\bwhat will\b.{0,40}\bprice\b", | |
| r"\bstock price\b.{0,50}\bwill\b", | |
| r"\bwill\b.{0,20}\bstock\b.{0,20}\b(be|reach|hit|go)\b", | |
| r"\bpredict\b.{0,30}\b(stock|price|winner|match|election|score)\b", | |
| r"\bforecast\b.{0,30}\b(price|stock|market|weather)\b", | |
| r"\bwhat will\b.{0,50}\bin\b.{0,5}\b20\d{2}\b", | |
| r"\bwho will\b.{0,30}\b(win|become|be elected|be president|be champion)\b", | |
| r"\bwho will be the next\b", | |
| r"\bwho will\b.{0,50}\b(next|new)\b.{0,20}\b(president|minister|prime minister|leader|chancellor|pm|ceo|governor)\b", | |
| r"\bwhich team will win\b", | |
| r"\bwill\b.{0,20}\bwin\b.{0,20}\b(championship|world cup|election|match|game|tournament)\b", | |
| r"\bwill\b.{0,30}\b(bitcoin|crypto|ethereum|stock|market)\b.{0,30}\b(reach|hit|go|be|rise|fall|crash)\b", | |
| r"\bwill\b.{0,20}\b(price|value)\b.{0,20}\b(reach|hit|go|be)\b", | |
| ] | |
| _FUTURE_RE = [re.compile(p, re.IGNORECASE | re.DOTALL) for p in _FUTURE_PATTERNS] | |
| UNCERTAIN_RESPONSE = ( | |
| "I don't have the ability to predict future events, prices, or outcomes. " | |
| "My knowledge has a cutoff date and I cannot reliably forecast what will happen. " | |
| "For up-to-date information please consult a reliable current source." | |
| ) | |
| # Patterns in the *output* that indicate a harmful generation slipped through | |
| _HARMFUL_OUTPUT_PATTERNS = [ | |
| r"\bkill\b.{0,60}\b(stab|strangle|shoot|poison|drown)\b", | |
| r"\bstep\s*1\b.{0,100}\b(kill|murder|stab|shoot)\b", | |
| r"(strangle|stab).{0,40}(neck|throat|body)", | |
| r"\bbest way to kill\b", | |
| r"\bhow to make.{0,20}(bomb|explosive)\b", | |
| ] | |
| _HARMFUL_OUT_RE = [re.compile(p, re.IGNORECASE | re.DOTALL) for p in _HARMFUL_OUTPUT_PATTERNS] | |
| def _is_harmful_input(text: str) -> bool: | |
| t = text.strip() | |
| return any(r.search(t) for r in _HARMFUL_RE) | |
| def _is_future_prediction(text: str) -> bool: | |
| return any(r.search(text.strip()) for r in _FUTURE_RE) | |
| def _is_harmful_output(text: str) -> bool: | |
| return any(r.search(text) for r in _HARMFUL_OUT_RE) | |
| def load_model(repo_id: str = REPO_ID, base_model: str = BASE_MODEL): | |
| from huggingface_hub import snapshot_download | |
| from models.alignx import build_alignx_model | |
| print(f"Downloading {repo_id} ...") | |
| local_dir = snapshot_download(repo_id) | |
| finetuned_paths = { | |
| ax: os.path.join(local_dir, f"lora_{ax}") | |
| for ax in ("helpful", "harmless", "honest") | |
| } | |
| tm_paths = { | |
| ax: os.path.join(local_dir, "task_vectors", f"T_{ax}.pt") | |
| for ax in ("helpful", "harmless", "honest") | |
| } | |
| model = build_alignx_model( | |
| base_model_name_or_path=base_model, | |
| finetuned_paths=finetuned_paths, | |
| task_matrix_paths=tm_paths, | |
| load_in_4bit=True, | |
| device_map="auto", | |
| ) | |
| mocae_dir = os.path.join(local_dir, "mocae_finetuning_plus_mocae") | |
| if os.path.exists(os.path.join(mocae_dir, "mocae.pt")): | |
| model.load_mocae(mocae_dir) | |
| tokenizer = AutoTokenizer.from_pretrained(base_model) | |
| if tokenizer.pad_token is None: | |
| tokenizer.pad_token = tokenizer.eos_token | |
| model.eval() | |
| return model, tokenizer | |
| def generate( | |
| model, | |
| tokenizer, | |
| instruction: str, | |
| max_new_tokens: int = 512, | |
| temperature: float = 0.7, | |
| ) -> str: | |
| if _is_harmful_input(instruction): | |
| return SAFE_REFUSAL | |
| if _is_future_prediction(instruction): | |
| return UNCERTAIN_RESPONSE | |
| prompt = PROMPT_TEMPLATE.format(instruction=instruction) | |
| inputs = tokenizer(prompt, return_tensors="pt") | |
| device = next(model.parameters()).device | |
| inputs = {k: v.to(device) for k, v in inputs.items()} | |
| prompt_len = inputs["input_ids"].shape[1] | |
| with torch.no_grad(): | |
| outputs = model.generate( | |
| **inputs, | |
| max_new_tokens=max_new_tokens, | |
| do_sample=temperature > 0, | |
| temperature=temperature, | |
| top_p=0.9, | |
| repetition_penalty=1.1, | |
| pad_token_id=tokenizer.pad_token_id, | |
| eos_token_id=tokenizer.eos_token_id, | |
| ) | |
| response = tokenizer.decode(outputs[0][prompt_len:], skip_special_tokens=True).strip() | |
| if _is_harmful_output(response): | |
| return SAFE_REFUSAL | |
| return response | |
| def interactive(model, tokenizer): | |
| print("AlignX Interactive Mode — type 'quit' to exit\n") | |
| while True: | |
| try: | |
| instruction = input(">>> ").strip() | |
| except (EOFError, KeyboardInterrupt): | |
| break | |
| if instruction.lower() in ("quit", "exit", "q"): | |
| break | |
| if not instruction: | |
| continue | |
| print(f"\n{generate(model, tokenizer, instruction)}\n") | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser(description="AlignX inference") | |
| parser.add_argument("--prompt", type=str, default=None, help="Single prompt") | |
| parser.add_argument("--interactive", action="store_true", help="Interactive mode") | |
| parser.add_argument("--max_new_tokens", type=int, default=512) | |
| parser.add_argument("--temperature", type=float, default=0.7) | |
| args = parser.parse_args() | |
| model, tokenizer = load_model() | |
| print("Model ready.\n") | |
| if args.interactive: | |
| interactive(model, tokenizer) | |
| elif args.prompt: | |
| print(generate(model, tokenizer, args.prompt, args.max_new_tokens, args.temperature)) | |
| else: | |
| demos = [ | |
| "Explain the concept of neural networks in simple terms.", | |
| "What are three tips for better sleep?", | |
| "Is the Great Wall of China visible from space?", | |
| ] | |
| for p in demos: | |
| print(f"Q: {p}") | |
| print(f"A: {generate(model, tokenizer, p)}\n") | |