import torch from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer from threading import Thread import re MODEL_PATH = "VDrontV2-0.1b-Title" TEMPERATURE = 0.4 MAX_NEW_TOKENS = 64 DEVICE = "cuda" if torch.cuda.is_available() else "cpu" STOP_TOKENS = ["<|endoftext|>", "(end_title)", "(title)", "\n\n\n"] STOP_PATTERNS = [ r"\(end_title\)", r"\(title\)", r"<\|endoftext\|>", ] def load_model_and_tokenizer(model_path): tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=False) model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32, device_map="auto", trust_remote_code=False ) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token if tokenizer.eos_token is None: tokenizer.eos_token = "<|endoftext|>" tokenizer.eos_token_id = tokenizer.convert_tokens_to_ids("<|endoftext|>") return model, tokenizer def should_stop(text, accumulated_text=""): full_text = accumulated_text + text for stop_token in STOP_TOKENS: if stop_token in full_text: return True, full_text.split(stop_token)[0] if len(full_text) > 100: last_part = full_text[-50:] if len(set(last_part.split())) <= 3: return True, full_text if len(full_text) > 60: for i in range(10, 30): pattern = full_text[-i:] if full_text.count(pattern) > 3: return True, full_text return False, full_text def generate_stream(model, tokenizer, prompt, temperature=0.4, max_new_tokens=256): inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=2048) inputs = {k: v.to(model.device) for k, v in inputs.items()} streamer = TextIteratorStreamer( tokenizer, skip_prompt=True, skip_special_tokens=False, timeout=30.0 ) generation_kwargs = dict( **inputs, max_new_tokens=max_new_tokens, temperature=temperature, do_sample=True, top_p=0.96, repetition_penalty=1.15, pad_token_id=tokenizer.pad_token_id, eos_token_id=tokenizer.eos_token_id, streamer=streamer, ) thread = Thread(target=model.generate, kwargs=generation_kwargs) thread.start() accumulated_text = "" try: for new_text in streamer: if new_text: accumulated_text += new_text stop, clean_text = should_stop(new_text, accumulated_text[:-len(new_text)] if len(accumulated_text) > len( new_text) else "") if stop: if clean_text and len(clean_text) > len(accumulated_text) - len(new_text): yield clean_text break partial_stop = False for stop_token in STOP_TOKENS: for i in range(1, len(stop_token)): if accumulated_text.endswith(stop_token[:i]): partial_stop = True break if partial_stop: break if not partial_stop: yield new_text if len(accumulated_text) > 50 and len(accumulated_text.split()) > 10: if any(pattern in accumulated_text for pattern in ["(end_title)", "(title)"]): break except Exception as e: print(f"\n[Stream error: {e}]") thread.join(timeout=5) def text_continuation(model, tokenizer, temperature): print(f"Instruct Mode (temp={temperature})") print("Enter your text and the model will continue it.") print("Commands: 'exit' or 'quit' — exit.") print(f"Stop tokens: {', '.join(STOP_TOKENS)}") print("=" * 60) while True: try: user_input = input("\nUser: ").strip() except (KeyboardInterrupt, EOFError): print("\nGoodbye!") break if user_input.lower() in ["exit", "quit"]: print("Goodbye!") break if not user_input: continue print("Title: ", end="", flush=True) formatted_prompt = f"(user){user_input}(title)" response_text = "" for token in generate_stream(model, tokenizer, formatted_prompt, temperature=temperature, max_new_tokens=MAX_NEW_TOKENS): print(token, end="", flush=True) response_text += token if len(response_text.split()) > 50: break print() if response_text: print(f"[Generated {len(response_text.split())} words]") if __name__ == "__main__": print(f"Loading model from {MODEL_PATH}...") model, tokenizer = load_model_and_tokenizer(MODEL_PATH) print(f"Model loaded. Device: {DEVICE}") print(f"Tokenizer eos_token: '{tokenizer.eos_token}'") print(f"Tokenizer eos_token_id: {tokenizer.eos_token_id}") text_continuation(model, tokenizer, temperature=TEMPERATURE)