""" LLM-powered naming suggestions for Trends and Forge tabs. Uses simple prompts for focused tasks - no complex agent loop. """ from __future__ import annotations import json import re from typing import Optional from app.config import get_settings from app.services.llm_gateway import chat_completions settings = get_settings() async def expand_trend_keywords(trend: str, geo: str = "US") -> list[str]: """ Given a trending topic, generate related domain-friendly keywords. Returns a list of 5-10 short, brandable keywords. """ prompt = f"""You are a domain naming expert. Given the trending topic "{trend}" (trending in {geo}), suggest 8-10 short, memorable keywords that would make good domain names. Rules: - Each keyword should be 4-10 characters - No spaces, hyphens, or special characters - Mix of: related words, abbreviations, creative variations - Think like a domain investor looking for valuable names Return ONLY a JSON array of lowercase strings, nothing else. Example: ["swiftie", "erastour", "taylormerch", "tswift"]""" try: res = await chat_completions({ "model": settings.llm_default_model, "messages": [{"role": "user", "content": prompt}], "temperature": 0.8, "stream": False, }) content = res.get("choices", [{}])[0].get("message", {}).get("content", "") # Extract JSON array from response match = re.search(r'\[.*?\]', content, re.DOTALL) if match: keywords = json.loads(match.group(0)) # Filter and clean return [ kw.lower().strip()[:15] for kw in keywords if isinstance(kw, str) and 3 <= len(kw.strip()) <= 15 and kw.isalnum() ][:10] except Exception as e: print(f"LLM keyword expansion failed: {e}") return [] async def analyze_trend(trend: str, geo: str = "US") -> str: """ Provide a brief analysis of why a trend is relevant for domain investors. Returns 2-3 sentences max. """ prompt = f"""You are a domain investing analyst. The topic "{trend}" is currently trending in {geo}. In 2-3 short sentences, explain: 1. Why this is trending (if obvious) 2. What domain opportunity this presents Be concise and actionable. No fluff.""" try: res = await chat_completions({ "model": settings.llm_default_model, "messages": [{"role": "user", "content": prompt}], "temperature": 0.5, "stream": False, }) content = res.get("choices", [{}])[0].get("message", {}).get("content", "") # Clean up and limit length content = content.strip()[:500] return content except Exception as e: print(f"LLM trend analysis failed: {e}") return "" async def generate_brandable_names( concept: str, style: Optional[str] = None, count: int = 15 ) -> list[str]: """ Generate brandable domain names based on a concept description. Args: concept: Description like "AI startup for legal documents" style: Optional style hint like "professional", "playful", "tech" count: Number of names to generate Returns list of brandable name suggestions (without TLD). """ style_hint = f" The style should be {style}." if style else "" prompt = f"""You are an expert brand naming consultant. Generate {count} unique, brandable domain names for: "{concept}"{style_hint} Rules: - Names must be 4-8 characters (shorter is better) - Easy to spell and pronounce - Memorable and unique - No dictionary words (invented names only) - Mix of patterns: CVCVC (Zalor), CVCCV (Bento), short words (Lyft) Return ONLY a JSON array of lowercase strings, nothing else. Example: ["zenix", "klaro", "voxly", "nimbl", "brivv"]""" try: res = await chat_completions({ "model": settings.llm_default_model, "messages": [{"role": "user", "content": prompt}], "temperature": 0.9, # Higher creativity "stream": False, }) content = res.get("choices", [{}])[0].get("message", {}).get("content", "") # Extract JSON array match = re.search(r'\[.*?\]', content, re.DOTALL) if match: names = json.loads(match.group(0)) # Filter and clean return [ name.lower().strip() for name in names if isinstance(name, str) and 3 <= len(name.strip()) <= 12 and name.isalnum() ][:count] except Exception as e: print(f"LLM brandable generation failed: {e}") return [] async def generate_similar_names(brand: str, count: int = 12) -> list[str]: """ Generate names similar to an existing brand. Useful for finding alternatives or inspired names. """ prompt = f"""You are a brand naming expert. Generate {count} new brandable names INSPIRED BY (but not copying) "{brand}". The names should: - Have similar length and rhythm to "{brand}" - Feel like they belong in the same industry - Be completely original (not existing brands) - Be 4-8 characters, easy to spell Return ONLY a JSON array of lowercase strings, nothing else.""" try: res = await chat_completions({ "model": settings.llm_default_model, "messages": [{"role": "user", "content": prompt}], "temperature": 0.85, "stream": False, }) content = res.get("choices", [{}])[0].get("message", {}).get("content", "") match = re.search(r'\[.*?\]', content, re.DOTALL) if match: names = json.loads(match.group(0)) return [ name.lower().strip() for name in names if isinstance(name, str) and 3 <= len(name.strip()) <= 12 and name.isalnum() ][:count] except Exception as e: print(f"LLM similar names failed: {e}") return []