VibecoderMcSwaggins commited on
Commit
fcc601a
Β·
1 Parent(s): 7323f55

fix: repair Gradio accordion bug and cleanup env vars

Browse files

- Unwrapped ChatInterface from gr.Blocks to fix settings accordion state (fixes P1_GRADIO_SETTINGS_CLEANUP).
- Updated .env.example with missing variables and removed dead ones.
- Updated documentation for the fix.

.env.example CHANGED
@@ -8,8 +8,16 @@ OPENAI_API_KEY=sk-your-key-here
8
  ANTHROPIC_API_KEY=sk-ant-your-key-here
9
 
10
  # Model names (optional - sensible defaults)
11
- OPENAI_MODEL=gpt-5.1
12
- ANTHROPIC_MODEL=claude-sonnet-4-5-20250929
 
 
 
 
 
 
 
 
13
 
14
  # ============== HUGGINGFACE (FREE TIER) ==============
15
 
@@ -20,7 +28,7 @@ ANTHROPIC_MODEL=claude-sonnet-4-5-20250929
20
  # WITH HF_TOKEN: Uses Llama 3.1 8B Instruct (requires accepting license)
21
  #
22
  # For HuggingFace Spaces deployment:
23
- # Set this as a "Secret" in Space Settings β†’ Variables and secrets
24
  # Users/judges don't need their own token - the Space secret is used
25
  #
26
  HF_TOKEN=hf_your-token-here
@@ -36,9 +44,5 @@ LOG_LEVEL=INFO
36
  # PubMed (optional - higher rate limits)
37
  NCBI_API_KEY=your-ncbi-key-here
38
 
39
- # Modal Sandbox (optional - for secure code execution)
40
- MODAL_TOKEN_ID=ak-your-modal-token-id-here
41
- MODAL_TOKEN_SECRET=your-modal-token-secret-here
42
-
43
  # Vector Database (optional - for LlamaIndex RAG)
44
  CHROMA_DB_PATH=./chroma_db
 
8
  ANTHROPIC_API_KEY=sk-ant-your-key-here
9
 
10
  # Model names (optional - sensible defaults)
11
+ OPENAI_MODEL=gpt-4-turbo
12
+ ANTHROPIC_MODEL=claude-3-5-sonnet-20240620
13
+
14
+ # ============== EMBEDDINGS ==============
15
+
16
+ # OpenAI Embedding Model (used if LLM_PROVIDER is openai and performing RAG/Embeddings)
17
+ OPENAI_EMBEDDING_MODEL=text-embedding-3-small
18
+
19
+ # Local Embedding Model (used for local/offline embeddings)
20
+ LOCAL_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
21
 
22
  # ============== HUGGINGFACE (FREE TIER) ==============
23
 
 
28
  # WITH HF_TOKEN: Uses Llama 3.1 8B Instruct (requires accepting license)
29
  #
30
  # For HuggingFace Spaces deployment:
31
+ # Set this as a "Secret" in Space Settings -> Variables and secrets
32
  # Users/judges don't need their own token - the Space secret is used
33
  #
34
  HF_TOKEN=hf_your-token-here
 
44
  # PubMed (optional - higher rate limits)
45
  NCBI_API_KEY=your-ncbi-key-here
46
 
 
 
 
 
47
  # Vector Database (optional - for LlamaIndex RAG)
48
  CHROMA_DB_PATH=./chroma_db
docs/brainstorming/01_PUBMED_IMPROVEMENTS.md ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PubMed Tool: Current State & Future Improvements
2
+
3
+ **Status**: Currently Implemented
4
+ **Priority**: High (Core Data Source)
5
+
6
+ ---
7
+
8
+ ## Current Implementation
9
+
10
+ ### What We Have (`src/tools/pubmed.py`)
11
+
12
+ - Basic E-utilities search via `esearch.fcgi` and `efetch.fcgi`
13
+ - Query preprocessing (strips question words, expands synonyms)
14
+ - Returns: title, abstract, authors, journal, PMID
15
+ - Rate limiting: None implemented (relying on NCBI defaults)
16
+
17
+ ### Current Limitations
18
+
19
+ 1. **No Full-Text Access**: Only retrieves abstracts, not full paper text
20
+ 2. **No Rate Limiting**: Risk of being blocked by NCBI
21
+ 3. **No BioC Format**: Missing structured full-text extraction
22
+ 4. **No Figure Retrieval**: No supplementary materials access
23
+ 5. **No PMC Integration**: Missing open-access full-text via PMC
24
+
25
+ ---
26
+
27
+ ## Reference Implementation (DeepCritical Reference Repo)
28
+
29
+ The reference repo at `reference_repos/DeepCritical/DeepResearch/src/tools/bioinformatics_tools.py` has a more sophisticated implementation:
30
+
31
+ ### Features We're Missing
32
+
33
+ ```python
34
+ # Rate limiting (lines 47-50)
35
+ from limits import parse
36
+ from limits.storage import MemoryStorage
37
+ from limits.strategies import MovingWindowRateLimiter
38
+
39
+ storage = MemoryStorage()
40
+ limiter = MovingWindowRateLimiter(storage)
41
+ rate_limit = parse("3/second") # NCBI allows 3/sec without API key, 10/sec with
42
+
43
+ # Full-text via BioC format (lines 108-120)
44
+ def _get_fulltext(pmid: int) -> dict[str, Any] | None:
45
+ pmid_url = f"https://www.ncbi.nlm.nih.gov/research/bionlp/RESTful/pmcoa.cgi/BioC_json/{pmid}/unicode"
46
+ # Returns structured JSON with full text for open-access papers
47
+
48
+ # Figure retrieval via Europe PMC (lines 123-149)
49
+ def _get_figures(pmcid: str) -> dict[str, str]:
50
+ suppl_url = f"https://www.ebi.ac.uk/europepmc/webservices/rest/{pmcid}/supplementaryFiles"
51
+ # Returns base64-encoded images from supplementary materials
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Recommended Improvements
57
+
58
+ ### Phase 1: Rate Limiting (Critical)
59
+
60
+ ```python
61
+ # Add to src/tools/pubmed.py
62
+ from limits import parse
63
+ from limits.storage import MemoryStorage
64
+ from limits.strategies import MovingWindowRateLimiter
65
+
66
+ storage = MemoryStorage()
67
+ limiter = MovingWindowRateLimiter(storage)
68
+
69
+ # With NCBI_API_KEY: 10/sec, without: 3/sec
70
+ def get_rate_limit():
71
+ if settings.ncbi_api_key:
72
+ return parse("10/second")
73
+ return parse("3/second")
74
+ ```
75
+
76
+ **Dependencies**: `pip install limits`
77
+
78
+ ### Phase 2: Full-Text Retrieval
79
+
80
+ ```python
81
+ async def get_fulltext(pmid: str) -> str | None:
82
+ """Get full text for open-access papers via BioC API."""
83
+ url = f"https://www.ncbi.nlm.nih.gov/research/bionlp/RESTful/pmcoa.cgi/BioC_json/{pmid}/unicode"
84
+ # Only works for PMC papers (open access)
85
+ ```
86
+
87
+ ### Phase 3: PMC ID Resolution
88
+
89
+ ```python
90
+ async def get_pmc_id(pmid: str) -> str | None:
91
+ """Convert PMID to PMCID for full-text access."""
92
+ url = f"https://www.ncbi.nlm.nih.gov/pmc/utils/idconv/v1.0/?ids={pmid}&format=json"
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Python Libraries to Consider
98
+
99
+ | Library | Purpose | Notes |
100
+ |---------|---------|-------|
101
+ | [Biopython](https://biopython.org/) | `Bio.Entrez` module | Official, well-maintained |
102
+ | [PyMed](https://pypi.org/project/pymed/) | PubMed wrapper | Simpler API, less control |
103
+ | [metapub](https://pypi.org/project/metapub/) | Full-featured | Tested on 1/3 of PubMed |
104
+ | [limits](https://pypi.org/project/limits/) | Rate limiting | Used by reference repo |
105
+
106
+ ---
107
+
108
+ ## API Endpoints Reference
109
+
110
+ | Endpoint | Purpose | Rate Limit |
111
+ |----------|---------|------------|
112
+ | `esearch.fcgi` | Search for PMIDs | 3/sec (10 with key) |
113
+ | `efetch.fcgi` | Fetch metadata | 3/sec (10 with key) |
114
+ | `esummary.fcgi` | Quick metadata | 3/sec (10 with key) |
115
+ | `pmcoa.cgi/BioC_json` | Full text (PMC only) | Unknown |
116
+ | `idconv/v1.0` | PMID ↔ PMCID | Unknown |
117
+
118
+ ---
119
+
120
+ ## Sources
121
+
122
+ - [PubMed E-utilities Documentation](https://www.ncbi.nlm.nih.gov/books/NBK25501/)
123
+ - [NCBI BioC API](https://www.ncbi.nlm.nih.gov/research/bionlp/APIs/)
124
+ - [Searching PubMed with Python](https://marcobonzanini.com/2015/01/12/searching-pubmed-with-python/)
125
+ - [PyMed on PyPI](https://pypi.org/project/pymed/)
docs/bugs/P1_GRADIO_SETTINGS_CLEANUP.md CHANGED
@@ -3,131 +3,79 @@
3
  **Priority**: P1 (UX Bug)
4
  **Status**: OPEN
5
  **Date**: 2025-11-27
 
6
 
7
  ---
8
 
9
- ## Bug Description
10
 
11
- The "Settings" accordion in the Gradio UI does not collapse/hide its content. Even when the accordion arrow shows "collapsed" state, all settings (Orchestrator Mode, API Key, API Provider) remain visible.
12
 
13
- ---
14
-
15
- ## Root Cause
16
 
17
- **Known Gradio Bug**: `additional_inputs_accordion` does not work correctly when `ChatInterface` is used inside `gr.Blocks()`.
18
 
19
- **GitHub Issue**: [gradio-app/gradio#8861](https://github.com/gradio-app/gradio/issues/8861)
20
- > "Is there any subsequent plan to support gr.ChatInterface inheritance under gr.Block()? Currently using accordion is not working well."
21
 
22
- **Our Code** (`src/app.py` lines 196-250):
23
- ```python
24
- with gr.Blocks(...) as demo: # <-- Using gr.Blocks wrapper
25
- gr.ChatInterface(
26
- ...
27
- additional_inputs_accordion=gr.Accordion(label="βš™οΈ Settings", open=False),
28
- additional_inputs=[...],
29
- )
30
- ```
31
 
32
- The `additional_inputs_accordion` parameter is designed for standalone `ChatInterface`, but breaks when wrapped in `gr.Blocks()`.
33
 
34
  ---
35
 
36
- ## Evidence
37
 
38
- - Accordion arrow toggles (visual feedback works)
39
- - Content does NOT hide when collapsed
40
- - Same behavior in local dev and HuggingFace Spaces
41
-
42
- ---
43
 
44
- ## Possible Fixes
45
 
46
- ### Option 1: Remove gr.Blocks Wrapper (Recommended)
47
 
48
- If we don't need the header/footer markdown, use standalone `ChatInterface`:
 
 
 
 
49
 
 
50
  ```python
51
- # Instead of gr.Blocks wrapper
52
- demo = gr.ChatInterface(
53
- fn=research_agent,
54
- title="🧬 DeepCritical",
55
- description="AI-Powered Drug Repurposing Agent",
56
- additional_inputs_accordion=gr.Accordion(label="βš™οΈ Settings", open=False),
57
- additional_inputs=[...],
58
- )
59
  ```
60
 
61
- **Pros**: Accordion should work correctly
62
- **Cons**: Less control over layout, no custom header/footer
63
-
64
- ### Option 2: Manual Accordion Outside ChatInterface
65
-
66
- Move settings outside `ChatInterface` into a proper `gr.Accordion`:
67
-
68
  ```python
69
- with gr.Blocks() as demo:
70
- gr.Markdown("# 🧬 DeepCritical")
71
-
72
- with gr.Accordion("βš™οΈ Settings", open=False):
73
- mode = gr.Radio(choices=["simple", "magentic"], value="simple", label="Mode")
74
- api_key = gr.Textbox(label="API Key", type="password")
75
- provider = gr.Radio(choices=["openai", "anthropic"], value="openai", label="Provider")
76
-
77
- chatbot = gr.Chatbot()
78
- msg = gr.Textbox(label="Ask a research question")
79
-
80
- msg.submit(research_agent, [msg, chatbot, mode, api_key, provider], chatbot)
81
- ```
82
-
83
- **Pros**: Full control, accordion works
84
- **Cons**: More code, lose ChatInterface conveniences (examples, etc.)
85
-
86
- ### Option 3: Wait for Gradio Fix
87
-
88
- Gradio added `.expand()` and `.collapse()` events in recent versions. Upgrading might help.
89
-
90
- **Check current version**:
91
- ```bash
92
- pip show gradio | grep Version
93
- ```
94
-
95
- **Upgrade**:
96
- ```bash
97
- pip install --upgrade gradio
98
  ```
99
 
100
  ---
101
 
102
- ## Recommendation
103
-
104
- **Option 1** (Remove gr.Blocks) is cleanest if we can live without custom header/footer.
105
-
106
- If header/footer needed, **Option 2** gives working accordion at cost of more code.
107
-
108
- ---
109
-
110
- ## Files to Modify
111
-
112
- | File | Change |
113
- |------|--------|
114
- | `src/app.py` | Restructure UI per chosen option |
115
- | `pyproject.toml` | Possibly upgrade Gradio version |
116
-
117
- ---
118
-
119
- ## Test Plan
120
 
121
- 1. Run locally: `uv run python -m src.app`
122
- 2. Click Settings accordion to collapse
123
- 3. Verify content hides when collapsed
124
- 4. Verify content shows when expanded
125
- 5. Test on HuggingFace Spaces after deploy
 
 
126
 
127
  ---
128
 
129
- ## Sources
130
 
131
- - [Gradio Issue #8861 - Accordion not working in Blocks](https://github.com/gradio-app/gradio/issues/8861)
132
- - [Gradio ChatInterface Docs](https://www.gradio.app/docs/gradio/chatinterface)
133
- - [Gradio Accordion Docs](https://www.gradio.app/docs/gradio/accordion)
 
3
  **Priority**: P1 (UX Bug)
4
  **Status**: OPEN
5
  **Date**: 2025-11-27
6
+ **Target Component**: `src/app.py`
7
 
8
  ---
9
 
10
+ ## 1. Problem Description
11
 
12
+ The "Settings" accordion in the Gradio UI (containing Orchestrator Mode, API Key, Provider) fails to collapse, even when configured with `open=False`. It remains permanently expanded, cluttering the interface and obscuring the chat history.
13
 
14
+ ### Symptoms
15
+ - Accordion arrow toggles visually, but content remains visible.
16
+ - Occurs in both local development (`uv run src/app.py`) and HuggingFace Spaces.
17
 
18
+ ---
19
 
20
+ ## 2. Root Cause Analysis
 
21
 
22
+ **Definitive Cause**: Nested `Blocks` Context Bug.
23
+ `gr.ChatInterface` is itself a high-level abstraction that creates a `gr.Blocks` context. Wrapping `gr.ChatInterface` inside an external `with gr.Blocks():` context causes event listener conflicts, specifically breaking the JavaScript state management for `additional_inputs_accordion`.
 
 
 
 
 
 
 
24
 
25
+ **Reference**: [Gradio Issue #8861](https://github.com/gradio-app/gradio/issues/8861) confirms that `additional_inputs_accordion` malfunctions when `ChatInterface` is not the top-level block.
26
 
27
  ---
28
 
29
+ ## 3. Solution Strategy: "The Unwrap Fix"
30
 
31
+ We will remove the redundant `gr.Blocks` wrapper. This restores the native behavior of `ChatInterface`, ensuring the accordion respects `open=False`.
 
 
 
 
32
 
33
+ ### Implementation Plan
34
 
35
+ **Refactor `src/app.py` / `create_demo()`**:
36
 
37
+ 1. **Remove** the `with gr.Blocks() as demo:` context manager.
38
+ 2. **Instantiate** `gr.ChatInterface` directly as the `demo` object.
39
+ 3. **Migrate UI Elements**:
40
+ * **Header**: Move the H1/Title text into the `title` parameter of `ChatInterface`.
41
+ * **Footer**: Move the footer text ("MCP Server Active...") into the `description` parameter. `ChatInterface` supports Markdown in `description`, making it the ideal place for static info below the title but above the chat.
42
 
43
+ ### Before (Buggy)
44
  ```python
45
+ def create_demo():
46
+ with gr.Blocks() as demo: # <--- CAUSE OF BUG
47
+ gr.Markdown("# Title")
48
+ gr.ChatInterface(..., additional_inputs_accordion=gr.Accordion(open=False))
49
+ gr.Markdown("Footer")
50
+ return demo
 
 
51
  ```
52
 
53
+ ### After (Correct)
 
 
 
 
 
 
54
  ```python
55
+ def create_demo():
56
+ return gr.ChatInterface( # <--- FIX: Top-level component
57
+ ...,
58
+ title="🧬 DeepCritical",
59
+ description="*AI-Powered Drug Repurposing Agent...*\n\n---\n**MCP Server Active**...",
60
+ additional_inputs_accordion=gr.Accordion(label="βš™οΈ Settings", open=False)
61
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  ```
63
 
64
  ---
65
 
66
+ ## 4. Validation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
+ 1. **Run**: `uv run python src/app.py`
69
+ 2. **Check**: Open `http://localhost:7860`
70
+ 3. **Verify**:
71
+ * Settings accordion starts **COLLAPSED**.
72
+ * Header title ("DeepCritical") is visible.
73
+ * Footer text ("MCP Server Active") is visible in the description area.
74
+ * Chat functionality works (Magentic/Simple modes).
75
 
76
  ---
77
 
78
+ ## 5. Constraints & Notes
79
 
80
+ - **Layout**: We lose the ability to place arbitrary elements *below* the chat box (footer will move to top, under title), but this is an acceptable trade-off for a working UI.
81
+ - **CSS**: `ChatInterface` handles its own CSS; any custom class styling from the previous footer will be standardized to the description text style.
 
src/app.py CHANGED
@@ -193,71 +193,59 @@ def create_demo() -> Any:
193
  Returns:
194
  Configured Gradio Blocks interface with MCP server enabled
195
  """
196
- with gr.Blocks(
197
- title="DeepCritical - Drug Repurposing Research Agent",
198
- ) as demo:
199
- # 1. Minimal Header (Option A: 2 lines max)
200
- gr.Markdown(
201
- "# 🧬 DeepCritical\n"
202
- "*AI-Powered Drug Repurposing Agent β€” searches PubMed, ClinicalTrials.gov & Europe PMC*"
203
- )
204
-
205
- # 2. Main Chat Interface
206
- # Config inputs will be in a collapsed accordion below the chat input
207
- gr.ChatInterface(
208
- fn=research_agent,
209
- examples=[
210
- [
211
- "What drugs could be repurposed for Alzheimer's disease?",
212
- "simple",
213
- "",
214
- "openai",
215
- ],
216
- [
217
- "Is metformin effective for treating cancer?",
218
- "simple",
219
- "",
220
- "openai",
221
- ],
222
- [
223
- "What medications show promise for Long COVID treatment?",
224
- "simple",
225
- "",
226
- "openai",
227
- ],
228
  ],
229
- additional_inputs_accordion=gr.Accordion(label="βš™οΈ Settings", open=False),
230
- additional_inputs=[
231
- gr.Radio(
232
- choices=["simple", "magentic"],
233
- value="simple",
234
- label="Orchestrator Mode",
235
- info="Simple: Linear | Magentic: Multi-Agent (OpenAI)",
236
- ),
237
- gr.Textbox(
238
- label="πŸ”‘ API Key (Optional - BYOK)",
239
- placeholder="sk-... or sk-ant-...",
240
- type="password",
241
- info="Enter your own API key. Never stored.",
242
- ),
243
- gr.Radio(
244
- choices=["openai", "anthropic"],
245
- value="openai",
246
- label="API Provider",
247
- info="Select the provider for your API key",
248
- ),
249
  ],
250
- )
251
-
252
- # 3. Minimal Footer (Option C: Remove MCP Tabs, keep info)
253
- gr.Markdown(
254
- """
255
- ---
256
- *Research tool only β€” not for medical advice.*
257
- **MCP Server Active**: Connect Claude Desktop to `/gradio_api/mcp/`
258
- """,
259
- elem_classes=["footer"],
260
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
  return demo
263
 
 
193
  Returns:
194
  Configured Gradio Blocks interface with MCP server enabled
195
  """
196
+ # 1. Unwrapped ChatInterface (Fixes Accordion Bug)
197
+ demo = gr.ChatInterface(
198
+ fn=research_agent,
199
+ title="🧬 DeepCritical",
200
+ description=(
201
+ "*AI-Powered Drug Repurposing Agent β€” searches PubMed, "
202
+ "ClinicalTrials.gov & Europe PMC*\n\n"
203
+ "---\n"
204
+ "*Research tool only β€” not for medical advice.* \n"
205
+ "**MCP Server Active**: Connect Claude Desktop to `/gradio_api/mcp/`"
206
+ ),
207
+ examples=[
208
+ [
209
+ "What drugs could be repurposed for Alzheimer's disease?",
210
+ "simple",
211
+ "",
212
+ "openai",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  ],
214
+ [
215
+ "Is metformin effective for treating cancer?",
216
+ "simple",
217
+ "",
218
+ "openai",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  ],
220
+ [
221
+ "What medications show promise for Long COVID treatment?",
222
+ "simple",
223
+ "",
224
+ "openai",
225
+ ],
226
+ ],
227
+ additional_inputs_accordion=gr.Accordion(label="βš™οΈ Settings", open=False),
228
+ additional_inputs=[
229
+ gr.Radio(
230
+ choices=["simple", "magentic"],
231
+ value="simple",
232
+ label="Orchestrator Mode",
233
+ info="Simple: Linear | Magentic: Multi-Agent (OpenAI)",
234
+ ),
235
+ gr.Textbox(
236
+ label="πŸ”‘ API Key (Optional - BYOK)",
237
+ placeholder="sk-... or sk-ant-...",
238
+ type="password",
239
+ info="Enter your own API key. Never stored.",
240
+ ),
241
+ gr.Radio(
242
+ choices=["openai", "anthropic"],
243
+ value="openai",
244
+ label="API Provider",
245
+ info="Select the provider for your API key",
246
+ ),
247
+ ],
248
+ )
249
 
250
  return demo
251