| """ |
| ui/components.py — reusable Gradio input widgets. |
| |
| Each function returns (component, …) tuples so app.py stays declarative. |
| """ |
|
|
| import gradio as gr |
|
|
| from config import ( |
| MODELS, |
| DEFAULT_PROMPT, |
| DEFAULT_MAX_TOKENS, |
| DEFAULT_TEMPERATURE, |
| ) |
|
|
| PROMPT_EXAMPLES = [ |
| ["Describe this image in detail."], |
| ["List every object you can see."], |
| ["What is the mood or atmosphere of this image?"], |
| ["What text, if any, appears in this image?"], |
| ["Explain this image to someone who cannot see it."], |
| ["What is unusual or interesting about this image?"], |
| ] |
|
|
|
|
| def image_upload() -> gr.Image: |
| return gr.Image(type="pil", label="📷 Input Image", height=320) |
|
|
|
|
| def prompt_box() -> gr.Textbox: |
| return gr.Textbox( |
| value=DEFAULT_PROMPT, |
| label="💬 Prompt", |
| lines=3, |
| placeholder="Ask anything about the image…", |
| ) |
|
|
|
|
| def model_selector() -> gr.Radio: |
| return gr.Radio( |
| choices=list(MODELS.keys()), |
| value=list(MODELS.keys())[0], |
| label="🤖 Model", |
| ) |
|
|
|
|
| def generation_settings() -> tuple[gr.Slider, gr.Slider]: |
| """Returns (max_tokens_slider, temperature_slider).""" |
| with gr.Accordion("⚙️ Generation settings", open=False): |
| max_tokens = gr.Slider( |
| 64, 2048, value=DEFAULT_MAX_TOKENS, step=64, label="Max tokens" |
| ) |
| temperature = gr.Slider( |
| 0.0, 1.5, value=DEFAULT_TEMPERATURE, step=0.05, label="Temperature" |
| ) |
| return max_tokens, temperature |
|
|
|
|
| def api_key_field() -> gr.Textbox: |
| with gr.Accordion("🔑 API Key", open=False): |
| box = gr.Textbox( |
| label="Your API key (optional)", |
| type="password", |
| lines=1, |
| placeholder="sk-… leave blank to use the free public key", |
| ) |
| gr.Markdown( |
| "A **free public key** is used when this field is blank. \n" |
| "It is shared and rate-limited — paste your own key from " |
| "[modelbest.cn](https://modelbest.cn) for unlimited use." |
| ) |
| return box |
|
|
|
|
| def prompt_examples(prompt_input: gr.Textbox) -> gr.Examples: |
| return gr.Examples( |
| examples=PROMPT_EXAMPLES, |
| inputs=[prompt_input], |
| label="📝 Examples", |
| ) |
|
|
|
|
| def output_box() -> gr.Textbox: |
| return gr.Textbox( |
| label="📄 Model Description (streaming)", |
| lines=22, |
| placeholder="Response streams here token-by-token…", |
| ) |
|
|
|
|
| def info_panel() -> None: |
| gr.Markdown( |
| "---\n" |
| "**API details**\n" |
| "- Endpoint: `https://api.modelbest.cn/v1/chat/completions`\n" |
| "- Protocol: OpenAI-compatible (SSE streaming)\n" |
| "- Auth: Bearer token\n\n" |
| "**Models**\n" |
| "| Model | Params | Best for |\n" |
| "|---|---|---|\n" |
| "| Instruct | 1.3 B | Fast Q&A, descriptions |\n" |
| "| Thinking | 1.3 B | Complex reasoning, OCR |\n" |
| ) |
|
|