🗞️ The Morris-Bot
Generate telecom industry news articles in the distinctive style of Iain Morris from Light Reading
""" Hugging Face Gradio App for Iain Morris Style Article Generation """ import gradio as gr import torch from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig from peft import PeftModel import logging import os # Set up logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class IainMorrisGenerator: def __init__(self): """Initialize the article generator""" self.base_model_name = "HuggingFaceH4/zephyr-7b-beta" self.lora_adapter_path = "models/iain-morris-model-enhanced" # Check for Apple Silicon MPS support if torch.backends.mps.is_available(): self.device = torch.device("mps") elif torch.cuda.is_available(): self.device = torch.device("cuda") else: self.device = torch.device("cpu") # Quantization config for inference (only for CUDA) if self.device.type == "cuda": self.bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16 ) else: self.bnb_config = None self.model = None self.tokenizer = None self.system_prompt = """You are Iain Morris, the cynical telecom journalist from Light Reading with a gift for doom-laden analysis and visceral metaphors. Your writing style is unmistakable: **PROVOCATIVE DOOM-LADEN OPENINGS**: Start with impending disaster, catastrophe, or failure - "What could possibly go wrong?" (signature phrase) - "train wreck", "disaster", "catastrophe", "nightmare scenario" - Present optimistic industry claims, then systematically demolish them **SIGNATURE DARK ANALOGIES**: Use visceral, physical metaphors for abstract concepts - Technology as disease, infection, or bodily harm - Business as warfare, natural disasters, or medical procedures - Markets as living organisms that can be sick, dying, or mutating **CYNICAL WIT & EXPERTISE**: Combine deep technical knowledge with cutting observations - Parenthetical snark (like this, but funnier and more cutting) - Quote industry executives, then undercut them immediately - Show technical expertise while mocking industry groupthink **DISTINCTIVE PHRASES**: Weave in signature expressions - "What could possibly go wrong?" - "train wreck" / "disaster" / "catastrophe" - "Meanwhile, back in reality..." - "Of course, there's a catch" / "But here's the thing" - References to "drinking the Kool-Aid" or similar **BRITISH CYNICISM**: Dry, cutting observations about human nature and corporate behavior - Assume the worst about corporate motives - Highlight contradictions and hypocrisy - Use understatement for dramatic effect Write a compelling article that captures this distinctive voice - cynical, expert, and darkly entertaining.""" def load_model(self): """Load the fine-tuned model""" try: logger.info("Loading tokenizer...") self.tokenizer = AutoTokenizer.from_pretrained( self.base_model_name, trust_remote_code=True, padding_side="left" ) if self.tokenizer.pad_token is None: self.tokenizer.pad_token = self.tokenizer.eos_token logger.info(f"Loading base model on device: {self.device}") # Configure model loading based on device model_kwargs = { "trust_remote_code": True, "torch_dtype": torch.bfloat16 } if self.device.type == "cuda": model_kwargs["quantization_config"] = self.bnb_config model_kwargs["device_map"] = "auto" else: # For MPS or CPU, load without quantization model_kwargs["device_map"] = None base_model = AutoModelForCausalLM.from_pretrained( self.base_model_name, **model_kwargs ) # Move to device if not using device_map if model_kwargs["device_map"] is None: base_model = base_model.to(self.device) # Load LoRA adapters if they exist if os.path.exists(self.lora_adapter_path): logger.info("Loading LoRA adapters...") self.model = PeftModel.from_pretrained(base_model, self.lora_adapter_path) else: logger.warning("LoRA adapters not found. Using base model.") self.model = base_model logger.info("Model loaded successfully!") return True except Exception as e: logger.error(f"Error loading model: {e}") return False def generate_article(self, topic: str, max_length: int = 1000, temperature: float = 0.7, top_p: float = 0.9) -> str: """ Generate an article on the given topic Args: topic: Topic to write about max_length: Maximum length of generated text temperature: Sampling temperature top_p: Top-p sampling parameter Returns: Generated article text """ if self.model is None or self.tokenizer is None: return "Error: Model not loaded. Please wait for the model to initialize." try: # Create the prompt messages = [ {"role": "system", "content": self.system_prompt}, {"role": "user", "content": f"Write a telecom industry news article about: {topic}"} ] # Format the prompt if hasattr(self.tokenizer, 'apply_chat_template'): try: prompt = self.tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True ) except: # Fallback formatting prompt = f"<|system|>\n{self.system_prompt}\n<|user|>\nWrite a telecom industry news article about: {topic}\n<|assistant|>\n" else: prompt = f"<|system|>\n{self.system_prompt}\n<|user|>\nWrite a telecom industry news article about: {topic}\n<|assistant|>\n" # Tokenize inputs = self.tokenizer( prompt, return_tensors="pt", truncation=True, max_length=1024 ).to(self.device) # Generate with torch.no_grad(): outputs = self.model.generate( **inputs, max_new_tokens=max_length, temperature=temperature, top_p=top_p, do_sample=True, pad_token_id=self.tokenizer.eos_token_id, repetition_penalty=1.1, no_repeat_ngram_size=3 ) # Decode generated_text = self.tokenizer.decode(outputs[0], skip_special_tokens=True) # Extract only the generated part (after the prompt) if "<|assistant|>" in generated_text: article = generated_text.split("<|assistant|>")[-1].strip() else: # Fallback: remove the original prompt article = generated_text[len(prompt):].strip() return article except Exception as e: logger.error(f"Error generating article: {e}") return f"Error generating article: {str(e)}" # Initialize the generator generator = IainMorrisGenerator() def generate_article_interface(topic, max_length, temperature, top_p): """Interface function for Gradio""" if not topic.strip(): return "Please enter a topic for the article." return generator.generate_article(topic, max_length, temperature, top_p) def load_model_interface(): """Load model interface for Gradio""" success = generator.load_model() if success: return "✅ Model loaded successfully! You can now generate articles." else: return "❌ Failed to load model. Please check the logs." # Create Gradio interface with gr.Blocks( title="Iain Morris Style Telecom Article Generator", theme=gr.themes.Soft(), css=""" .main-header { text-align: center; background: linear-gradient(90deg, #1e3a8a, #3b82f6); color: white; padding: 2rem; border-radius: 10px; margin-bottom: 2rem; } .description { text-align: center; font-size: 1.1em; color: #4b5563; margin-bottom: 2rem; } """ ) as demo: gr.HTML("""
Generate telecom industry news articles in the distinctive style of Iain Morris from Light Reading
This AI model has been fine-tuned on articles by Iain Morris to capture his analytical writing style, technical expertise, and slightly irreverent tone. Enter a telecom topic below to generate an article in his distinctive voice.
⚠️ Please Note: Article generation may take several minutes due to the use of Hugging Face's free CPU infrastructure. Thank you for your patience!
About: This model generates articles in the style of Iain Morris, a respected telecom journalist known for his insightful analysis and accessible explanations of complex technical topics.
Generated content is AI-created and should be reviewed before publication.