#!/usr/bin/env python3 """ Chat with this model on your own machine. pip install -r requirements.txt python run_model.py # multi-turn chat python run_model.py "When did WWII end?" # one answer, then exit python run_model.py --model # a model somewhere else By default the model is loaded from the directory this script sits in, so a plain download of the repo is enough: hf download --local-dir model && cd model && python run_model.py Runs on CPU (no GPU, no flash-attn needed); uses CUDA automatically when available. Greedy decoding (temperature 0) is the default and gives this model's most reliable answers. In-chat commands: /reset start a new conversation (forget the history) /temp N set the sampling temperature (0 = greedy) /length N set the maximum tokens per reply /help show the commands /quit exit (Ctrl-C or Ctrl-D also work) """ import argparse import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) COMMANDS = """Commands: /reset start a new conversation /temp N sampling temperature (0 = greedy, the default) /length N maximum tokens per reply /help show this list /quit exit""" def resolve_model(model): """A local directory must hold a config.json; anything that looks like `user/name` is treated as a Hub repo id (downloaded on first use).""" if os.path.isdir(model): if os.path.exists(os.path.join(model, "config.json")): return model sys.exit(f"No model at {model} (no config.json there).\n" "Run this script from a downloaded model directory, or pass " "--model .") if "/" in model and not model.startswith((".", "/", "~")): return model sys.exit(f"No model at {model} (not a directory, not a repo id).") def main(): p = argparse.ArgumentParser( description="Chat with this model locally.", epilog=COMMANDS, formatter_class=argparse.RawDescriptionHelpFormatter) p.add_argument("prompt", nargs="*", help="A single question (omit for interactive chat).") p.add_argument("--model", default=HERE, help="Model directory or Hub repo id (default: this script's directory).") p.add_argument("--temperature", type=float, default=0.0, help="0 = greedy (recommended). Higher adds errors and drift.") p.add_argument("--max-new-tokens", type=int, default=2048, help="Maximum tokens per reply (default: 2048; generation stops earlier " "when the answer ends).") p.add_argument("--fp32", action="store_true", help="Run the model in float32 on CUDA instead of the default bf16 " "(CPU is always float32).") args = p.parse_args() model_path = resolve_model(args.model) import torch from transformers import AutoModelForCausalLM, AutoTokenizer device = "cuda" if torch.cuda.is_available() else "cpu" # bf16 on CUDA unless --fp32; CPU is always float32. dtype = torch.bfloat16 if (device == "cuda" and not args.fp32) else torch.float32 print(f"Loading {model_path} ({device}, {str(dtype).split('.')[-1]})...") tok = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_path, trust_remote_code=True, dtype=dtype).to(device).eval() temperature = args.temperature max_new_tokens = args.max_new_tokens def reply(messages): ids = torch.tensor(tok.build_chatml_ids(messages, add_generation_prompt=True)) out = model.chat_generate(ids, max_new_tokens=max_new_tokens, temperature=temperature) return tok.decode_response(out) if args.prompt: print(reply([{"role": "user", "content": " ".join(args.prompt)}])) return print("Ready. Type a message; /help lists the commands.\n") messages = [] # the whole conversation is re-sent each turn while True: try: text = input("You: ").strip() except (EOFError, KeyboardInterrupt): print() return if not text: continue if text.startswith("/"): cmd, _, arg = text.partition(" ") cmd = cmd.lower() if cmd == "/quit": return elif cmd == "/reset": messages = [] print("New conversation.") elif cmd == "/temp": try: temperature = float(arg) print(f"Temperature {temperature:g}" + (" (greedy)" if temperature < 0.01 else "")) except ValueError: print("Usage: /temp N (e.g. /temp 0 or /temp 0.7)") elif cmd == "/length": try: max_new_tokens = int(arg) print(f"Max tokens per reply {max_new_tokens}") except ValueError: print("Usage: /length N (e.g. /length 512)") elif cmd == "/help": print(COMMANDS) else: print(f"Unknown command {cmd}.\n{COMMANDS}") continue messages.append({"role": "user", "content": text}) try: answer = reply(messages) except KeyboardInterrupt: messages.pop() print("\n(interrupted)\n") continue messages.append({"role": "assistant", "content": answer}) print(f"\nModel: {answer}\n") if __name__ == "__main__": main()