""" Analyze user prompts, select the appropriate SDK, and plan app architecture. """ import re from typing import Optional # Keywords that strongly indicate a particular SDK GRADIO_KEYWORDS = [ "gradio", "demo", "interface", "chatbot", "chat", "image classifier", "image classification", "object detection", "text generation", "summarizer", "summarization", "sentiment", "translation", "translate", "speech", "audio", "video", "upload", "webcam", "microphone", "slider", "dropdown", "interactive", "ml model", "machine learning", "deep learning", "neural", "predict", "inference", "classify", "segment", "detect", "recognize", "generate image", "stable diffusion", "text-to-image", "image-to-text", "question answering", "qa", "ner", "named entity", "token classification", "zero-shot", "few-shot", "embedding", "similarity", ] DOCKER_KEYWORDS = [ "docker", "api", "rest", "fastapi", "flask", "endpoint", "websocket", "agent", "autonomous", "pipeline", "workflow", "backend", "server", "database", "redis", "celery", "queue", "cron", "schedule", "scrape", "crawl", "multi-step", "orchestrat", "microservice", "webhook", "authentication", "auth", "oauth", "custom server", "complex", "real-time", "streaming api", ] STATIC_KEYWORDS = [ "static", "landing page", "portfolio", "documentation", "docs", "blog", "website", "html", "css only", "showcase", "gallery", "resume", "cv", "personal site", "no backend", "frontend only", ] # Templates for common app types APP_TEMPLATES = { "chatbot": { "components": ["chat_interface", "system_prompt_config", "clear_button", "history"], "model_task": "text-generation", "description": "Conversational AI chatbot", }, "image_classifier": { "components": ["image_upload", "label_output", "confidence_bars", "examples"], "model_task": "image-classification", "description": "Image classification with confidence scores", }, "text_summarizer": { "components": ["text_input", "file_upload", "length_selector", "text_output"], "model_task": "summarization", "description": "Text summarization tool", }, "sentiment_analyzer": { "components": ["text_input", "label_output", "chart_output"], "model_task": "text-classification", "description": "Sentiment analysis with visualization", }, "text_generator": { "components": ["text_input", "parameter_controls", "text_output"], "model_task": "text-generation", "description": "Text generation with configurable parameters", }, "translator": { "components": ["text_input", "language_selector", "text_output"], "model_task": "translation", "description": "Multi-language translation tool", }, "object_detector": { "components": ["image_upload", "annotated_image_output", "json_output"], "model_task": "object-detection", "description": "Object detection with bounding boxes", }, "speech_to_text": { "components": ["audio_input", "text_output", "language_selector"], "model_task": "automatic-speech-recognition", "description": "Speech recognition / transcription", }, "image_generator": { "components": ["text_input", "parameter_controls", "image_output", "gallery"], "model_task": "text-to-image", "description": "AI image generation from text prompts", }, "question_answering": { "components": ["context_input", "question_input", "answer_output", "highlight"], "model_task": "question-answering", "description": "Extractive question answering", }, "rest_api": { "components": ["fastapi_app", "model_endpoint", "docs", "health_check"], "model_task": "text-generation", "description": "REST API serving ML models", }, "portfolio": { "components": ["hero_section", "projects_section", "skills_section", "contact_form"], "model_task": None, "description": "Personal portfolio website", }, } class AppPlanner: """Analyzes user prompts and produces a structured app plan.""" def analyze(self, prompt: str, sdk_preference: str = "auto") -> dict: """ Analyze the user prompt and return a structured plan. Returns dict with keys: sdk, app_name, app_type, description, components, model_task, template_key, title """ prompt_lower = prompt.lower() # Detect the best matching template template_key = self._match_template(prompt_lower) template = APP_TEMPLATES.get(template_key, {}) # Determine SDK if sdk_preference != "auto": sdk = sdk_preference else: sdk = self._select_sdk(prompt_lower, template_key) # Generate an app name from the prompt app_name = self._generate_app_name(prompt_lower) # Determine a human-readable title title = self._generate_title(prompt, template_key) plan = { "sdk": sdk, "app_name": app_name, "app_type": template_key or "custom", "title": title, "description": template.get("description", self._extract_description(prompt)), "components": template.get("components", self._infer_components(prompt_lower, sdk)), "model_task": template.get("model_task", self._infer_model_task(prompt_lower)), "template_key": template_key, "original_prompt": prompt, } return plan def _select_sdk(self, prompt_lower: str, template_key: Optional[str]) -> str: """Auto-select the best SDK based on prompt analysis.""" # Check template-based hints if template_key in ("rest_api",): return "docker" if template_key in ("portfolio",): return "static" gradio_score = sum(1 for kw in GRADIO_KEYWORDS if kw in prompt_lower) docker_score = sum(1 for kw in DOCKER_KEYWORDS if kw in prompt_lower) static_score = sum(1 for kw in STATIC_KEYWORDS if kw in prompt_lower) scores = {"gradio": gradio_score, "docker": docker_score, "static": static_score} best = max(scores, key=scores.get) # Default to gradio if no strong signal if scores[best] == 0: return "gradio" return best def _match_template(self, prompt_lower: str) -> Optional[str]: """Find the best matching template for the prompt.""" scores = {} keyword_map = { "chatbot": ["chat", "chatbot", "conversation", "dialogue", "talk"], "image_classifier": ["classify image", "image classif", "image recogni", "predict image"], "text_summarizer": ["summar", "summarize", "summarization", "tldr", "shorten text"], "sentiment_analyzer": ["sentiment", "emotion", "mood", "opinion"], "text_generator": ["text generat", "write text", "generate text", "story generat", "complete text"], "translator": ["translat", "language translation"], "object_detector": ["object detect", "detect object", "bounding box", "find objects"], "speech_to_text": ["speech", "transcri", "voice to text", "audio to text", "asr"], "image_generator": ["generate image", "text to image", "stable diffusion", "create image", "dall-e", "image generat"], "question_answering": ["question answer", "qa ", "answer question", "reading comprehension"], "rest_api": ["rest api", "api service", "endpoint", "api server", "fastapi"], "portfolio": ["portfolio", "personal site", "resume", "cv site", "showcase"], } for key, keywords in keyword_map.items(): score = sum(1 for kw in keywords if kw in prompt_lower) if score > 0: scores[key] = score if not scores: return None return max(scores, key=scores.get) def _generate_app_name(self, prompt_lower: str) -> str: """Generate a slug-style app name.""" # Try to extract key nouns words = re.sub(r"[^a-z0-9\s]", "", prompt_lower).split() stop_words = { "a", "an", "the", "is", "are", "was", "were", "be", "been", "being", "have", "has", "had", "do", "does", "did", "will", "would", "could", "should", "may", "might", "can", "shall", "build", "create", "make", "develop", "design", "generate", "that", "which", "who", "whom", "this", "these", "those", "i", "me", "my", "we", "our", "you", "your", "it", "its", "they", "them", "their", "what", "where", "when", "how", "and", "or", "but", "if", "then", "else", "so", "for", "with", "from", "to", "of", "in", "on", "at", "by", "about", "into", "through", "during", "before", "after", "above", "below", "between", "under", "using", "use", "uses", "let", "users", "user", "app", "application", } meaningful = [w for w in words if w not in stop_words and len(w) > 2][:4] if not meaningful: meaningful = ["my", "app"] return "-".join(meaningful) def _generate_title(self, prompt: str, template_key: Optional[str]) -> str: """Generate a human-friendly title.""" if template_key and template_key in APP_TEMPLATES: return APP_TEMPLATES[template_key]["description"] # Capitalize first ~6 words of prompt words = prompt.split()[:6] title = " ".join(words) if len(prompt.split()) > 6: title += "..." return title def _extract_description(self, prompt: str) -> str: """Extract a short description from the prompt.""" sentences = prompt.split(".") if sentences: return sentences[0].strip()[:200] return prompt[:200] def _infer_components(self, prompt_lower: str, sdk: str) -> list: """Infer UI components from the prompt when no template matches.""" components = [] if sdk == "gradio": if any(w in prompt_lower for w in ["image", "photo", "picture", "upload image"]): components.append("image_upload") if any(w in prompt_lower for w in ["text", "input", "enter", "paste", "type"]): components.append("text_input") if any(w in prompt_lower for w in ["audio", "sound", "voice", "microphone"]): components.append("audio_input") if any(w in prompt_lower for w in ["video", "webcam"]): components.append("video_input") if any(w in prompt_lower for w in ["chart", "plot", "graph", "visual"]): components.append("chart_output") if any(w in prompt_lower for w in ["file", "upload", "download"]): components.append("file_component") if not components: components = ["text_input", "text_output"] elif sdk == "docker": components = ["fastapi_app", "model_endpoint", "health_check"] else: components = ["html_page", "css_styles", "js_scripts"] return components def _infer_model_task(self, prompt_lower: str) -> Optional[str]: """Infer the HF model task from prompt keywords.""" task_keywords = { "text-generation": ["generat", "write", "complete", "chat", "story"], "text-classification": ["classif", "sentiment", "emotion", "categor"], "summarization": ["summar", "tldr", "shorten"], "translation": ["translat"], "image-classification": ["image classif", "recognize image", "identify image"], "object-detection": ["object detect", "bounding box", "detect object"], "text-to-image": ["generate image", "text to image", "create image"], "automatic-speech-recognition": ["speech", "transcri", "asr", "voice to text"], "question-answering": ["question answer", "qa"], "token-classification": ["ner", "named entity", "token classif"], "fill-mask": ["fill mask", "mask", "cloze"], } for task, keywords in task_keywords.items(): if any(kw in prompt_lower for kw in keywords): return task return "text-generation" # sensible default