AlignX / inference.py
Gautam Kashyap
Add honest override: future prediction queries return uncertainty response
7b8c6f2 verified
Raw
History Blame Contribute Delete
10.5 kB
"""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")