Spaces:
Running
Running
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 +16 -0
- src/orchestrators/simple.py +13 -3
- tests/unit/orchestrators/test_simple_synthesis.py +7 -3
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 (
|
|
|
|
| 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 |
-
#
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|