| import os |
| from collections.abc import Generator |
| from typing import Any |
|
|
| import gradio as gr |
| from dotenv import load_dotenv |
| from groq import Groq |
|
|
| |
| |
| |
| |
| try: |
| import spaces |
| except ImportError: |
| |
| class _SpacesFallback: |
| @staticmethod |
| def GPU(*args: Any, **kwargs: Any): |
| def decorator(function): |
| return function |
|
|
| return decorator |
|
|
| spaces = _SpacesFallback() |
|
|
| |
| |
| load_dotenv() |
|
|
| DEFAULT_SYSTEM_PROMPT = ( |
| "You are a helpful, accurate, and friendly AI assistant. " |
| "Answer clearly, use Markdown when helpful, and admit uncertainty when needed." |
| ) |
|
|
| MODEL_CHOICES = [ |
| "openai/gpt-oss-20b", |
| "openai/gpt-oss-120b", |
| ] |
|
|
| CUSTOM_CSS = """ |
| .gradio-container { |
| max-width: 1050px !important; |
| margin: 0 auto !important; |
| } |
| #app-header { |
| text-align: center; |
| padding: 12px 0 4px 0; |
| } |
| #app-subtitle { |
| text-align: center; |
| opacity: 0.8; |
| margin-bottom: 10px; |
| } |
| footer { |
| display: none !important; |
| } |
| """ |
|
|
|
|
| def _text_history(history: list[dict[str, Any]]) -> list[dict[str, str]]: |
| """Keep only plain-text user and assistant messages for the Groq API.""" |
| cleaned: list[dict[str, str]] = [] |
|
|
| for item in history or []: |
| role = item.get("role") |
| content = item.get("content") |
|
|
| if role in {"user", "assistant"} and isinstance(content, str): |
| cleaned.append({"role": role, "content": content}) |
|
|
| return cleaned |
|
|
|
|
| @spaces.GPU(duration=1) |
| def zero_gpu_compatibility_check() -> str: |
| """Allow startup on ZeroGPU; the Groq chat itself remains CPU/API based.""" |
| return "ZeroGPU compatibility ready" |
|
|
|
|
| def chat_with_groq( |
| message: str, |
| history: list[dict[str, Any]], |
| model: str, |
| system_prompt: str, |
| temperature: float, |
| max_tokens: int, |
| ) -> Generator[str, None, None]: |
| """Stream a Groq response to the Gradio chat interface.""" |
| api_key = os.getenv("GROQ_API_KEY") |
|
|
| if not api_key: |
| yield ( |
| "### Missing API key\n\n" |
| "Add a Hugging Face Space secret named `GROQ_API_KEY`, then restart the Space." |
| ) |
| return |
|
|
| user_message = (message or "").strip() |
| if not user_message: |
| yield "Please enter a message." |
| return |
|
|
| selected_model = model if model in MODEL_CHOICES else MODEL_CHOICES[0] |
| instructions = (system_prompt or "").strip() or DEFAULT_SYSTEM_PROMPT |
|
|
| messages: list[dict[str, str]] = [ |
| {"role": "system", "content": instructions}, |
| *_text_history(history), |
| {"role": "user", "content": user_message}, |
| ] |
|
|
| try: |
| client = Groq(api_key=api_key) |
| stream = client.chat.completions.create( |
| model=selected_model, |
| messages=messages, |
| temperature=float(temperature), |
| max_completion_tokens=int(max_tokens), |
| stream=True, |
| ) |
|
|
| response = "" |
| for chunk in stream: |
| delta = chunk.choices[0].delta.content |
| if delta: |
| response += delta |
| yield response |
|
|
| if not response: |
| yield "The model returned an empty response. Please try again." |
|
|
| except Exception as error: |
| |
| error_name = type(error).__name__ |
| yield ( |
| "### Request failed\n\n" |
| f"`{error_name}`: {error}\n\n" |
| "Check your Groq API key, model access, account limits, and network connection." |
| ) |
|
|
|
|
| with gr.Blocks(title="Groq AI Chat") as demo: |
| |
| |
| |
| zero_gpu_trigger = gr.Button(visible=False) |
| zero_gpu_status = gr.Textbox(visible=False) |
| zero_gpu_trigger.click( |
| fn=zero_gpu_compatibility_check, |
| inputs=None, |
| outputs=zero_gpu_status, |
| api_visibility="private", |
| ) |
|
|
| gr.Markdown("# ⚡ Groq AI Chat", elem_id="app-header") |
| gr.Markdown( |
| "A fast, streaming chatbot powered by Groq and built with Gradio.", |
| elem_id="app-subtitle", |
| ) |
|
|
| with gr.Accordion("Chat settings", open=False): |
| model_input = gr.Dropdown( |
| choices=MODEL_CHOICES, |
| value=MODEL_CHOICES[0], |
| label="Groq model", |
| info="GPT-OSS 20B is faster; GPT-OSS 120B is stronger for complex tasks.", |
| ) |
| system_prompt_input = gr.Textbox( |
| value=DEFAULT_SYSTEM_PROMPT, |
| label="System prompt", |
| lines=3, |
| ) |
| with gr.Row(): |
| temperature_input = gr.Slider( |
| minimum=0.0, |
| maximum=2.0, |
| value=0.7, |
| step=0.1, |
| label="Temperature", |
| ) |
| max_tokens_input = gr.Slider( |
| minimum=128, |
| maximum=4096, |
| value=1024, |
| step=128, |
| label="Maximum response tokens", |
| ) |
|
|
| chatbot = gr.Chatbot( |
| label="Conversation", |
| placeholder="Ask anything to begin the conversation.", |
| height=520, |
| ) |
|
|
| gr.ChatInterface( |
| fn=chat_with_groq, |
| chatbot=chatbot, |
| additional_inputs=[ |
| model_input, |
| system_prompt_input, |
| temperature_input, |
| max_tokens_input, |
| ], |
| examples=[ |
| [ |
| "Explain artificial intelligence in simple words.", |
| MODEL_CHOICES[0], |
| DEFAULT_SYSTEM_PROMPT, |
| 0.7, |
| 1024, |
| ], |
| [ |
| "Write a professional email requesting a meeting.", |
| MODEL_CHOICES[0], |
| DEFAULT_SYSTEM_PROMPT, |
| 0.7, |
| 1024, |
| ], |
| [ |
| "Create a beginner-friendly Python learning plan.", |
| MODEL_CHOICES[1], |
| DEFAULT_SYSTEM_PROMPT, |
| 0.5, |
| 1536, |
| ], |
| ], |
| editable=True, |
| save_history=True, |
| flagging_mode="never", |
| api_visibility="private", |
| concurrency_limit=5, |
| fill_height=True, |
| fill_width=True, |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| demo.queue(default_concurrency_limit=5).launch( |
| theme=gr.themes.Soft(), |
| css=CUSTOM_CSS, |
| ) |
|
|