""" Generate Gradio app code using the HF Inference API (LLM-powered). Falls back to template-based generation if the API is unavailable. """ import os import re from huggingface_hub import InferenceClient LLM_MODEL = "Qwen/Qwen2.5-72B-Instruct" # Pre-built templates for common app types (used as fallback and as LLM context) GRADIO_TEMPLATES = { "chatbot": '''import gradio as gr from huggingface_hub import InferenceClient client = InferenceClient("{model_id}") def respond(message, history, system_message, max_tokens, temperature, top_p): messages = [{{"role": "system", "content": system_message}}] for user_msg, bot_msg in history: if user_msg: messages.append({{"role": "user", "content": user_msg}}) if bot_msg: messages.append({{"role": "assistant", "content": bot_msg}}) messages.append({{"role": "user", "content": message}}) response = "" for chunk in client.chat_completion( messages, max_tokens=max_tokens, stream=True, temperature=temperature, top_p=top_p, ): token = chunk.choices[0].delta.content or "" response += token yield response demo = gr.ChatInterface( respond, additional_inputs=[ gr.Textbox(value="You are a helpful assistant.", label="System message"), gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max tokens"), gr.Slider(minimum=0.1, maximum=2.0, value=0.7, step=0.1, label="Temperature"), gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p"), ], title="{title}", description="{description}", ) if __name__ == "__main__": demo.launch() ''', "image_classifier": '''import gradio as gr from huggingface_hub import InferenceClient import json client = InferenceClient("{model_id}") def classify_image(image): if image is None: return {{}} result = client.image_classification(image) return {{item["label"]: round(item["score"], 4) for item in result}} demo = gr.Interface( fn=classify_image, inputs=gr.Image(type="filepath", label="Upload Image"), outputs=gr.Label(num_top_classes=5, label="Predictions"), title="{title}", description="{description}", examples=[], allow_flagging="never", ) if __name__ == "__main__": demo.launch() ''', "text_summarizer": '''import gradio as gr from huggingface_hub import InferenceClient client = InferenceClient("{model_id}") def summarize(text, max_length, min_length): if not text.strip(): return "Please enter some text to summarize." result = client.summarization( text, parameters={{"max_length": int(max_length), "min_length": int(min_length)}}, ) return result["summary_text"] if isinstance(result, dict) else result[0]["summary_text"] demo = gr.Interface( fn=summarize, inputs=[ gr.Textbox(lines=10, placeholder="Paste your text here...", label="Input Text"), gr.Slider(50, 500, value=150, step=10, label="Max Summary Length"), gr.Slider(10, 100, value=30, step=5, label="Min Summary Length"), ], outputs=gr.Textbox(lines=5, label="Summary"), title="{title}", description="{description}", allow_flagging="never", ) if __name__ == "__main__": demo.launch() ''', "sentiment_analyzer": '''import gradio as gr from huggingface_hub import InferenceClient client = InferenceClient("{model_id}") def analyze_sentiment(text): if not text.strip(): return {{}}, "" result = client.text_classification(text) scores = {{item["label"]: round(item["score"], 4) for item in result}} top_label = max(scores, key=scores.get) explanation = f"The text is predominantly **{{top_label}}** (confidence: {{scores[top_label]:.1%}})" return scores, explanation demo = gr.Interface( fn=analyze_sentiment, inputs=gr.Textbox(lines=5, placeholder="Enter text to analyze...", label="Input Text"), outputs=[ gr.Label(label="Sentiment Scores"), gr.Markdown(label="Analysis"), ], title="{title}", description="{description}", allow_flagging="never", ) if __name__ == "__main__": demo.launch() ''', "text_generator": '''import gradio as gr from huggingface_hub import InferenceClient client = InferenceClient("{model_id}") def generate_text(prompt, max_tokens, temperature, top_p): if not prompt.strip(): return "Please enter a prompt." messages = [{{"role": "user", "content": prompt}}] response = "" for chunk in client.chat_completion( messages, max_tokens=int(max_tokens), stream=True, temperature=temperature, top_p=top_p, ): token = chunk.choices[0].delta.content or "" response += token yield response demo = gr.Interface( fn=generate_text, inputs=[ gr.Textbox(lines=3, placeholder="Enter your prompt...", label="Prompt"), gr.Slider(50, 2048, value=512, step=50, label="Max Tokens"), gr.Slider(0.1, 2.0, value=0.7, step=0.1, label="Temperature"), gr.Slider(0.1, 1.0, value=0.95, step=0.05, label="Top-p"), ], outputs=gr.Textbox(lines=10, label="Generated Text"), title="{title}", description="{description}", allow_flagging="never", ) if __name__ == "__main__": demo.launch() ''', "translator": '''import gradio as gr from huggingface_hub import InferenceClient client = InferenceClient("{model_id}") def translate(text, target_language): if not text.strip(): return "Please enter text to translate." prompt = f"Translate the following text to {{target_language}}:\\n\\n{{text}}" messages = [{{"role": "user", "content": prompt}}] result = client.chat_completion(messages, max_tokens=1024) return result.choices[0].message.content LANGUAGES = ["French", "Spanish", "German", "Italian", "Portuguese", "Chinese", "Japanese", "Korean", "Arabic", "Hindi", "Russian"] demo = gr.Interface( fn=translate, inputs=[ gr.Textbox(lines=5, placeholder="Enter text to translate...", label="Input Text"), gr.Dropdown(choices=LANGUAGES, value="French", label="Target Language"), ], outputs=gr.Textbox(lines=5, label="Translation"), title="{title}", description="{description}", allow_flagging="never", ) if __name__ == "__main__": demo.launch() ''', "question_answering": '''import gradio as gr from huggingface_hub import InferenceClient client = InferenceClient("{model_id}") def answer_question(context, question): if not context.strip() or not question.strip(): return "Please provide both context and a question." result = client.question_answering(question=question, context=context) answer = result["answer"] score = result["score"] return f"**Answer:** {{answer}}\\n\\n**Confidence:** {{score:.1%}}" demo = gr.Interface( fn=answer_question, inputs=[ gr.Textbox(lines=8, placeholder="Paste the context here...", label="Context"), gr.Textbox(lines=2, placeholder="Ask a question...", label="Question"), ], outputs=gr.Markdown(label="Answer"), title="{title}", description="{description}", allow_flagging="never", ) if __name__ == "__main__": demo.launch() ''', } class GradioGenerator: """Generate Gradio app.py code for a given plan.""" def __init__(self): self._client = None @property def client(self) -> InferenceClient: if self._client is None: token = os.environ.get("HF_TOKEN", None) self._client = InferenceClient(LLM_MODEL, token=token) return self._client def generate(self, plan: dict, prompt: str) -> str: """Generate app.py content for a Gradio Space.""" template_key = plan.get("template_key") model_id = self._get_model_id(plan) title = plan.get("title", "My App") description = plan.get("description", "") # First try LLM generation for best results try: code = self._generate_with_llm(plan, prompt, model_id, title, description) if code and len(code) > 100: return code except Exception: pass # Fallback to template-based generation if template_key and template_key in GRADIO_TEMPLATES: return GRADIO_TEMPLATES[template_key].format( model_id=model_id, title=title, description=description, ) # Final fallback: generic Gradio app return self._generate_generic(plan, model_id, title, description) def _generate_with_llm( self, plan: dict, prompt: str, model_id: str, title: str, description: str ) -> str: """Use the LLM to generate custom Gradio app code.""" template_key = plan.get("template_key") reference_code = "" if template_key and template_key in GRADIO_TEMPLATES: reference_code = GRADIO_TEMPLATES[template_key].format( model_id=model_id, title=title, description=description, ) system_prompt = """You are an expert Python developer specializing in Gradio applications for Hugging Face Spaces. You generate complete, working app.py files that are production-ready. Rules: 1. Output ONLY the Python code, no explanations or markdown. 2. Use `huggingface_hub.InferenceClient` for model inference (NOT transformers or pipeline). 3. Always include proper error handling. 4. The app must use `demo.launch()` at the end. 5. Include descriptive title and description in the interface. 6. Use appropriate Gradio components for the use case. 7. Code must be complete and runnable as-is. 8. Do NOT use `transformers`, `torch`, or `tensorflow` imports - use only `huggingface_hub.InferenceClient`. 9. For streaming text generation, use `client.chat_completion(..., stream=True)`. 10. For image tasks, use `client.image_classification()`, `client.object_detection()`, etc. 11. For text tasks, use `client.text_classification()`, `client.summarization()`, etc.""" user_prompt = f"""Generate a complete Gradio app.py for this request: USER REQUEST: {prompt} APP PLAN: - SDK: gradio - Type: {plan.get('app_type', 'custom')} - Model: {model_id} - Model Task: {plan.get('model_task', 'text-generation')} - Components: {', '.join(plan.get('components', []))} - Title: {title} - Description: {description} - Extra Features: {', '.join(plan.get('extra_features', []))} """ if reference_code: user_prompt += f""" REFERENCE TEMPLATE (adapt and improve this based on the user's specific request): ```python {reference_code} ``` """ user_prompt += "\nGenerate the complete app.py code now:" response = self.client.chat_completion( messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], max_tokens=4096, temperature=0.3, ) raw = response.choices[0].message.content return self._extract_code(raw) def _extract_code(self, text: str) -> str: """Extract Python code from LLM response.""" # Try to find code blocks code_blocks = re.findall(r"```(?:python)?\s*\n(.*?)```", text, re.DOTALL) if code_blocks: # Return the longest code block return max(code_blocks, key=len).strip() # If no code blocks, check if the whole response looks like Python code lines = text.strip().split("\n") if lines and (lines[0].startswith("import ") or lines[0].startswith("from ")): return text.strip() return text.strip() def _get_model_id(self, plan: dict) -> str: """Get the best model ID from the plan.""" models = plan.get("recommended_models", []) if models: return models[0]["id"] # Fallback defaults by task task_defaults = { "text-generation": "Qwen/Qwen2.5-7B-Instruct", "text-classification": "cardiffnlp/twitter-roberta-base-sentiment-latest", "summarization": "facebook/bart-large-cnn", "translation": "Qwen/Qwen2.5-7B-Instruct", "image-classification": "google/vit-base-patch16-224", "object-detection": "facebook/detr-resnet-50", "text-to-image": "stabilityai/stable-diffusion-xl-base-1.0", "automatic-speech-recognition": "openai/whisper-base", "question-answering": "deepset/roberta-base-squad2", } return task_defaults.get(plan.get("model_task", ""), "Qwen/Qwen2.5-7B-Instruct") def _generate_generic(self, plan: dict, model_id: str, title: str, description: str) -> str: """Generate a generic Gradio app when no template matches.""" task = plan.get("model_task", "text-generation") if task in ("text-generation",): return GRADIO_TEMPLATES["text_generator"].format( model_id=model_id, title=title, description=description, ) elif task in ("text-classification",): return GRADIO_TEMPLATES["sentiment_analyzer"].format( model_id=model_id, title=title, description=description, ) elif task in ("summarization",): return GRADIO_TEMPLATES["text_summarizer"].format( model_id=model_id, title=title, description=description, ) elif task in ("image-classification",): return GRADIO_TEMPLATES["image_classifier"].format( model_id=model_id, title=title, description=description, ) elif task in ("question-answering",): return GRADIO_TEMPLATES["question_answering"].format( model_id=model_id, title=title, description=description, ) else: return GRADIO_TEMPLATES["text_generator"].format( model_id=model_id, title=title, description=description, ) def edit(self, plan: dict, current_code: str, edit_prompt: str) -> str: """Edit existing Gradio code based on user instructions.""" try: system_prompt = """You are an expert Python developer. You will receive existing Gradio app code and an edit request. Modify the code according to the request and return the COMPLETE updated code. Output ONLY the Python code, no explanations or markdown fences. Keep all existing functionality unless the user explicitly asks to remove something. Use huggingface_hub.InferenceClient for model inference.""" user_prompt = f"""EXISTING CODE: ```python {current_code} ``` EDIT REQUEST: {edit_prompt} Return the complete updated app.py code:""" response = self.client.chat_completion( messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], max_tokens=4096, temperature=0.2, ) raw = response.choices[0].message.content code = self._extract_code(raw) if code and len(code) > 50: return code except Exception: pass return current_code