VibecoderMcSwaggins commited on
Commit
ad823e0
Β·
1 Parent(s): 1d32642

feat(ui): force dark mode for api key input and enhance synthesis error handling

Browse files
src/app.py CHANGED
@@ -257,10 +257,25 @@ def create_demo() -> tuple[gr.ChatInterface, gr.Accordion]:
257
  "**MCP Server Active**: Connect Claude Desktop to `/gradio_api/mcp/`"
258
  )
259
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  demo = gr.ChatInterface(
261
  fn=research_agent,
262
  title="πŸ† DeepBoner",
263
  description=description,
 
264
  examples=[
265
  [
266
  "What drugs improve female libido post-menopause?",
@@ -304,6 +319,7 @@ def create_demo() -> tuple[gr.ChatInterface, gr.Accordion]:
304
  placeholder="sk-... (OpenAI) or sk-ant-... (Anthropic)",
305
  type="password",
306
  info="Leave empty for free tier. Auto-detects provider from key prefix.",
 
307
  ),
308
  api_key_state, # Hidden state component for persistence
309
  ],
 
257
  "**MCP Server Active**: Connect Claude Desktop to `/gradio_api/mcp/`"
258
  )
259
 
260
+ # CSS to force dark mode on API key input
261
+ custom_css = """
262
+ .api-key-input input {
263
+ background-color: #1f2937 !important;
264
+ color: white !important;
265
+ border-color: #374151 !important;
266
+ }
267
+ .api-key-input input:focus {
268
+ background-color: #1f2937 !important;
269
+ color: white !important;
270
+ border-color: #e879f9 !important;
271
+ }
272
+ """
273
+
274
  demo = gr.ChatInterface(
275
  fn=research_agent,
276
  title="πŸ† DeepBoner",
277
  description=description,
278
+ css=custom_css,
279
  examples=[
280
  [
281
  "What drugs improve female libido post-menopause?",
 
319
  placeholder="sk-... (OpenAI) or sk-ant-... (Anthropic)",
320
  type="password",
321
  info="Leave empty for free tier. Auto-detects provider from key prefix.",
322
+ elem_classes=["api-key-input"],
323
  ),
324
  api_key_state, # Hidden state component for persistence
325
  ],
src/orchestrators/simple.py CHANGED
@@ -541,11 +541,13 @@ class Orchestrator:
541
 
542
  from src.agent_factory.judges import get_model
543
 
544
- # Create synthesis agent (string output, not structured)
 
545
  agent: Agent[None, str] = Agent(
546
  model=get_model(),
547
  output_type=str,
548
  system_prompt=system_prompt,
 
549
  )
550
  result = await agent.run(user_prompt)
551
  narrative = result.output
@@ -554,14 +556,22 @@ class Orchestrator:
554
 
555
  except Exception as e:
556
  # Fallback to template synthesis if LLM fails
557
- # This is intentionally broad - LLM can fail many ways (API, parsing, etc.)
558
  logger.warning(
559
  "LLM synthesis failed, using template fallback",
560
  error=str(e),
561
  exc_type=type(e).__name__,
562
  evidence_count=len(evidence),
563
  )
564
- return self._generate_template_synthesis(query, evidence, assessment)
 
 
 
 
 
 
 
 
565
 
566
  # Add full citation list footer
567
  citations = "\n".join(
 
541
 
542
  from src.agent_factory.judges import get_model
543
 
544
+ # Create synthesis agent with retries (matching Judge agent pattern)
545
+ # Without retries, transient errors immediately trigger fallback
546
  agent: Agent[None, str] = Agent(
547
  model=get_model(),
548
  output_type=str,
549
  system_prompt=system_prompt,
550
+ retries=3, # Match Judge agent - retry on transient errors
551
  )
552
  result = await agent.run(user_prompt)
553
  narrative = result.output
 
556
 
557
  except Exception as e:
558
  # Fallback to template synthesis if LLM fails
559
+ # Log error details for debugging
560
  logger.warning(
561
  "LLM synthesis failed, using template fallback",
562
  error=str(e),
563
  exc_type=type(e).__name__,
564
  evidence_count=len(evidence),
565
  )
566
+ # Surface the error to user (MS Agent Framework pattern)
567
+ # Don't silently fall back - let user know synthesis degraded
568
+ error_note = (
569
+ f"\n\n> ⚠️ **Note**: AI narrative synthesis unavailable. "
570
+ f"Showing structured summary.\n"
571
+ f"> _Error: {type(e).__name__}_\n"
572
+ )
573
+ template = self._generate_template_synthesis(query, evidence, assessment)
574
+ return f"{error_note}\n{template}"
575
 
576
  # Add full citation list footer
577
  citations = "\n".join(
tests/unit/orchestrators/test_simple_synthesis.py CHANGED
@@ -130,12 +130,12 @@ Long-term safety data is limited.
130
  assert "Evidence Synthesis" in result
131
 
132
  @pytest.mark.asyncio
133
- async def test_falls_back_on_llm_error(
134
  self,
135
  sample_evidence: list[Evidence],
136
  sample_assessment: JudgeAssessment,
137
  ) -> None:
138
- """Synthesis should fall back to template if LLM fails."""
139
  mock_search = MagicMock()
140
  mock_judge = MagicMock()
141
 
@@ -155,7 +155,11 @@ Long-term safety data is limited.
155
  assessment=sample_assessment,
156
  )
157
 
158
- # Should return template fallback (has Assessment section)
 
 
 
 
159
  assert "Assessment" in result or "Drug Candidates" in result
160
  assert "Testosterone" in result # Drug candidate should be present
161
 
 
130
  assert "Evidence Synthesis" in result
131
 
132
  @pytest.mark.asyncio
133
+ async def test_falls_back_on_llm_error_with_notice(
134
  self,
135
  sample_evidence: list[Evidence],
136
  sample_assessment: JudgeAssessment,
137
  ) -> None:
138
+ """Synthesis should fall back to template if LLM fails, WITH error notice."""
139
  mock_search = MagicMock()
140
  mock_judge = MagicMock()
141
 
 
155
  assessment=sample_assessment,
156
  )
157
 
158
+ # Should surface error to user (MS Agent Framework pattern)
159
+ assert "AI narrative synthesis unavailable" in result
160
+ assert "Error" in result
161
+
162
+ # Should still include template content
163
  assert "Assessment" in result or "Drug Candidates" in result
164
  assert "Testosterone" in result # Drug candidate should be present
165