| # UI Simplification: Remove API Provider Dropdown | |
| **Issues**: #52, #53 | |
| **Priority**: P1 - UX improvement for hackathon demo | |
| **Estimated Time**: 30 minutes | |
| **Senior Review**: β Approved with changes (incorporated below) | |
| --- | |
| ## Problem | |
| The current UI has confusing BYOK (Bring Your Own Key) settings: | |
| 1. **Provider dropdown is misleading** - Shows "openai" but actually uses free tier when no key | |
| 2. **Examples table shows useless columns** - API Key (empty), Provider (ignored) | |
| 3. **Anthropic doesn't work with Advanced mode** - Only OpenAI has `agent-framework` support | |
| ## Solution | |
| Remove `api_provider` dropdown entirely. Auto-detect provider from key prefix. | |
| **Functionality preserved:** | |
| - Simple mode: Free tier, OpenAI, OR Anthropic (all work) | |
| - Advanced mode: OpenAI only (Magentic multi-agent requires `OpenAIChatClient`) | |
| --- | |
| ## Implementation | |
| ### File: `src/app.py` | |
| #### Change 1: Update `configure_orchestrator()` signature (lines 23-28) | |
| ```python | |
| # BEFORE | |
| def configure_orchestrator( | |
| use_mock: bool = False, | |
| mode: str = "simple", | |
| user_api_key: str | None = None, | |
| api_provider: str = "openai", # β REMOVE | |
| ) -> tuple[Any, str]: | |
| # AFTER | |
| def configure_orchestrator( | |
| use_mock: bool = False, | |
| mode: str = "simple", | |
| user_api_key: str | None = None, | |
| ) -> tuple[Any, str]: | |
| ``` | |
| #### Change 2: Update docstring (lines 29-40) | |
| ```python | |
| # AFTER | |
| """ | |
| Create an orchestrator instance. | |
| Args: | |
| use_mock: If True, use MockJudgeHandler (no API key needed) | |
| mode: Orchestrator mode ("simple" or "advanced") | |
| user_api_key: Optional user-provided API key (BYOK) - auto-detects provider | |
| Returns: | |
| Tuple of (Orchestrator instance, backend_name) | |
| """ | |
| ``` | |
| #### Change 3: Replace provider logic with auto-detection (lines 62-88) | |
| ```python | |
| # BEFORE (lines 62-88) - complex provider checking with api_provider param | |
| # AFTER - auto-detect from key prefix | |
| # 2. Paid API Key (User provided or Env) | |
| elif user_api_key and user_api_key.strip(): | |
| # Auto-detect provider from key prefix | |
| model: AnthropicModel | OpenAIModel | |
| if user_api_key.startswith("sk-ant-"): | |
| # Anthropic key | |
| anthropic_provider = AnthropicProvider(api_key=user_api_key) | |
| model = AnthropicModel(settings.anthropic_model, provider=anthropic_provider) | |
| backend_info = "Paid API (Anthropic)" | |
| elif user_api_key.startswith("sk-"): | |
| # OpenAI key | |
| openai_provider = OpenAIProvider(api_key=user_api_key) | |
| model = OpenAIModel(settings.openai_model, provider=openai_provider) | |
| backend_info = "Paid API (OpenAI)" | |
| else: | |
| raise ValueError( | |
| "Invalid API key format. Expected sk-... (OpenAI) or sk-ant-... (Anthropic)" | |
| ) | |
| judge_handler = JudgeHandler(model=model) | |
| # 3. Environment API Keys (fallback) | |
| elif os.getenv("OPENAI_API_KEY"): | |
| judge_handler = JudgeHandler(model=None) # Uses env key | |
| backend_info = "Paid API (OpenAI from env)" | |
| elif os.getenv("ANTHROPIC_API_KEY"): | |
| judge_handler = JudgeHandler(model=None) # Uses env key | |
| backend_info = "Paid API (Anthropic from env)" | |
| # 4. Free Tier (HuggingFace Inference) | |
| else: | |
| judge_handler = HFInferenceJudgeHandler() | |
| backend_info = "Free Tier (Llama 3.1 / Mistral)" | |
| ``` | |
| #### Change 4: Update `research_agent()` signature (lines 105-111) | |
| ```python | |
| # BEFORE | |
| async def research_agent( | |
| message: str, | |
| history: list[dict[str, Any]], | |
| mode: str = "simple", | |
| api_key: str = "", | |
| api_provider: str = "openai", # β REMOVE | |
| ) -> AsyncGenerator[str, None]: | |
| # AFTER | |
| async def research_agent( | |
| message: str, | |
| history: list[dict[str, Any]], | |
| mode: str = "simple", | |
| api_key: str = "", | |
| ) -> AsyncGenerator[str, None]: | |
| ``` | |
| #### Change 5: Update docstring (lines 112-124) | |
| ```python | |
| # AFTER | |
| """ | |
| Gradio chat function that runs the research agent. | |
| Args: | |
| message: User's research question | |
| history: Chat history (Gradio format) | |
| mode: Orchestrator mode ("simple" or "advanced") | |
| api_key: Optional user-provided API key (BYOK - auto-detects provider) | |
| Yields: | |
| Markdown-formatted responses for streaming | |
| """ | |
| ``` | |
| #### Change 6: Fix Advanced mode check (line 139) | |
| ```python | |
| # BEFORE | |
| if mode == "advanced" and not (has_openai or (has_user_key and api_provider == "openai")): | |
| # AFTER - auto-detect OpenAI key from prefix | |
| is_openai_user_key = user_api_key and user_api_key.startswith("sk-") and not user_api_key.startswith("sk-ant-") | |
| if mode == "advanced" and not (has_openai or is_openai_user_key): | |
| yield ( | |
| "β οΈ **Advanced mode requires OpenAI API key.** " | |
| "Anthropic keys only work in Simple mode. Falling back to Simple.\n\n" | |
| ) | |
| mode = "simple" | |
| ``` | |
| #### Change 7: Remove premature "Using your key" message (lines 146-151) | |
| ```python | |
| # BEFORE - uses api_provider which no longer exists | |
| if has_user_key: | |
| yield ( | |
| f"π **Using your {api_provider.upper()} API key** - " | |
| "Your key is used only for this session and is never stored.\n\n" | |
| ) | |
| # AFTER - remove this block entirely | |
| # The backend_name from configure_orchestrator already shows "Paid API (OpenAI)" or "Paid API (Anthropic)" | |
| # No need for duplicate messaging | |
| ``` | |
| #### Change 8: Update configure_orchestrator call (lines 165-170) | |
| ```python | |
| # BEFORE | |
| orchestrator, backend_name = configure_orchestrator( | |
| use_mock=False, | |
| mode=mode, | |
| user_api_key=user_api_key, | |
| api_provider=api_provider, # β REMOVE | |
| ) | |
| # AFTER | |
| orchestrator, backend_name = configure_orchestrator( | |
| use_mock=False, | |
| mode=mode, | |
| user_api_key=user_api_key, | |
| ) | |
| ``` | |
| #### Change 9: Simplify examples (lines 210-229) | |
| ```python | |
| # BEFORE - 4 items per example | |
| examples=[ | |
| ["What drugs improve female libido post-menopause?", "simple", "", "openai"], | |
| ["Clinical trials for erectile dysfunction alternatives to PDE5 inhibitors?", "simple", "", "openai"], | |
| ["Evidence for testosterone therapy in women with HSDD?", "simple", "", "openai"], | |
| ], | |
| # AFTER - 2 items per example (query, mode) - API key always empty in examples | |
| examples=[ | |
| ["What drugs improve female libido post-menopause?", "simple"], | |
| ["Clinical trials for ED alternatives to PDE5 inhibitors?", "simple"], | |
| ["Evidence for testosterone therapy in women with HSDD?", "simple"], | |
| ], | |
| ``` | |
| #### Change 10: Update additional_inputs (lines 231-252) | |
| ```python | |
| # BEFORE - 3 inputs (mode, api_key, api_provider) | |
| additional_inputs=[ | |
| gr.Radio( | |
| choices=["simple", "advanced"], | |
| value="simple", | |
| label="Orchestrator Mode", | |
| info="Simple: Linear (Free Tier Friendly) | Advanced: Multi-Agent (Requires OpenAI)", | |
| ), | |
| gr.Textbox( | |
| label="π API Key (Optional - BYOK)", | |
| placeholder="sk-... or sk-ant-...", | |
| type="password", | |
| info="Enter your own API key. Never stored.", | |
| ), | |
| gr.Radio( # β REMOVE THIS ENTIRE BLOCK | |
| choices=["openai", "anthropic"], | |
| value="openai", | |
| label="API Provider", | |
| info="Select the provider for your API key", | |
| ), | |
| ], | |
| # AFTER - 2 inputs (mode, api_key) | |
| additional_inputs=[ | |
| gr.Radio( | |
| choices=["simple", "advanced"], | |
| value="simple", | |
| label="Orchestrator Mode", | |
| info="Simple: Works with any key or free tier | Advanced: Requires OpenAI key", | |
| ), | |
| gr.Textbox( | |
| label="π API Key (Optional)", | |
| placeholder="sk-... (OpenAI) or sk-ant-... (Anthropic)", | |
| type="password", | |
| info="Leave empty for free tier. Auto-detects provider from key prefix.", | |
| ), | |
| ], | |
| ``` | |
| #### Change 11: Update accordion label (line 230) | |
| ```python | |
| # BEFORE | |
| additional_inputs_accordion=gr.Accordion(label="βοΈ Settings", open=False), | |
| # AFTER | |
| additional_inputs_accordion=gr.Accordion(label="βοΈ Settings (Free tier works without API key)", open=False), | |
| ``` | |
| --- | |
| ## Testing Checklist | |
| ### Manual Tests | |
| - [ ] **No key**: Shows "Free Tier (Llama 3.1 / Mistral)" in backend | |
| - [ ] **OpenAI key (sk-...)**: Shows "Paid API (OpenAI)" in backend | |
| - [ ] **Anthropic key (sk-ant-...)**: Shows "Paid API (Anthropic)" in backend | |
| - [ ] **Invalid key format**: Shows error message | |
| - [ ] **Anthropic key + Advanced mode**: Falls back to Simple with warning | |
| - [ ] **OpenAI key + Advanced mode**: Uses full Magentic multi-agent | |
| - [ ] **Examples table**: Shows only 2 columns (query, mode) | |
| - [ ] **MCP server**: Still accessible at `/gradio_api/mcp/` | |
| ### Unit Test Updates | |
| - [ ] `tests/unit/test_app_smoke.py` - may need update if checking input count | |
| --- | |
| ## Definition of Done | |
| - [ ] `api_provider` parameter removed from `configure_orchestrator()` | |
| - [ ] `api_provider` parameter removed from `research_agent()` | |
| - [ ] Auto-detection logic works for `sk-` and `sk-ant-` prefixes | |
| - [ ] Advanced mode check uses auto-detection (not removed param) | |
| - [ ] "Using your X key" message removed (backend_name handles this) | |
| - [ ] Examples table shows 2 columns | |
| - [ ] Accordion label updated | |
| - [ ] Placeholder text shows both key formats | |
| - [ ] All existing tests pass | |
| - [ ] MCP server still works | |
| --- | |
| ## Mode Compatibility Matrix (Unchanged) | |
| | Mode | No Key | OpenAI Key | Anthropic Key | | |
| |------|--------|------------|---------------| | |
| | **Simple** | β Free tier | β GPT-5.1 | β Claude Sonnet 4.5 | | |
| | **Advanced** | β οΈ Falls back | β Full Magentic | β οΈ Falls back to Simple | | |
| --- | |
| ## Related | |
| - Issue #52: UI Polish - Examples table confusion | |
| - Issue #53: API Provider Simplification | |
| - Senior Review: Approved 2025-11-28 | |