Spaces:
Running
Running
Joseph Pollack
commited on
adds final fixes
Browse files- AUDIO_INPUT_FIX.md +1 -0
- ERROR_FIXES_SUMMARY.md +1 -0
- FILE_OUTPUT_IMPLEMENTATION_PLAN.md +1 -0
- FILE_OUTPUT_VERIFICATION.md +1 -0
- FIX_SUMMARY.md +100 -0
- MULTIMODAL_SETTINGS_IMPLEMENTATION_PLAN.md +1 -0
- MULTIMODAL_SETTINGS_IMPLEMENTATION_SUMMARY.md +1 -0
- README.md +49 -20
- REPORT_WRITING_AGENTS_ANALYSIS.md +1 -0
- SERPER_WEBSEARCH_IMPLEMENTATION_PLAN.md +1 -0
- dev/__init__.py +1 -0
- docs/api/agents.md +1 -0
- docs/api/models.md +1 -0
- docs/api/services.md +1 -0
- docs/api/tools.md +1 -0
- docs/architecture/agents.md +1 -0
- docs/contributing/code-quality.md +1 -0
- docs/contributing/code-style.md +1 -0
- docs/contributing/error-handling.md +1 -0
- docs/contributing/implementation-patterns.md +1 -0
- docs/contributing/index.md +1 -0
- docs/contributing/prompt-engineering.md +1 -0
- docs/contributing/testing.md +1 -0
- docs/getting-started/installation.md +1 -0
- docs/implementation/IMPLEMENTATION_SUMMARY.md +1 -0
- docs/implementation/TTS_MODAL_IMPLEMENTATION.md +1 -0
- docs/license.md +1 -0
- docs/team.md +1 -0
- new_env.txt +1 -0
- src/app.py +17 -13
- src/middleware/state_machine.py +1 -0
- src/orchestrator/graph_orchestrator.py +79 -6
- src/services/report_file_service.py +1 -0
- src/tools/searchxng_web_search.py +1 -0
- src/tools/serper_web_search.py +1 -0
- src/tools/vendored/crawl_website.py +1 -0
- src/tools/vendored/searchxng_client.py +1 -0
- src/tools/vendored/serper_client.py +1 -0
- src/tools/vendored/web_search_core.py +1 -0
- src/tools/web_search_factory.py +1 -0
- tests/unit/middleware/test_budget_tracker_phase7.py +1 -0
- tests/unit/middleware/test_state_machine.py +1 -0
- tests/unit/middleware/test_workflow_manager.py +1 -0
AUDIO_INPUT_FIX.md
CHANGED
|
@@ -89,3 +89,4 @@ If audio input still doesn't appear:
|
|
| 89 |
- The `file_types` parameter ensures audio files are accepted for upload
|
| 90 |
|
| 91 |
|
|
|
|
|
|
| 89 |
- The `file_types` parameter ensures audio files are accepted for upload
|
| 90 |
|
| 91 |
|
| 92 |
+
|
ERROR_FIXES_SUMMARY.md
CHANGED
|
@@ -151,3 +151,4 @@ Some MCP tools use `gr.State` inputs, which Gradio warns won't update between to
|
|
| 151 |
4. Document the tuple format handling for future reference
|
| 152 |
|
| 153 |
|
|
|
|
|
|
| 151 |
4. Document the tuple format handling for future reference
|
| 152 |
|
| 153 |
|
| 154 |
+
|
FILE_OUTPUT_IMPLEMENTATION_PLAN.md
CHANGED
|
@@ -236,3 +236,4 @@ Current implementation in `event_to_chat_message()` already handles this correct
|
|
| 236 |
|
| 237 |
|
| 238 |
|
|
|
|
|
|
| 236 |
|
| 237 |
|
| 238 |
|
| 239 |
+
|
FILE_OUTPUT_VERIFICATION.md
CHANGED
|
@@ -219,3 +219,4 @@ The implementation is:
|
|
| 219 |
No reimplementation needed. All changes are present and correct.
|
| 220 |
|
| 221 |
|
|
|
|
|
|
| 219 |
No reimplementation needed. All changes are present and correct.
|
| 220 |
|
| 221 |
|
| 222 |
+
|
FIX_SUMMARY.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Fix Summary: Research Results Not Returned to User
|
| 2 |
+
|
| 3 |
+
## Problem
|
| 4 |
+
The application was returning "Research completed" instead of the actual research report content to users. Reports were being generated and saved to files, but the final result wasn't being properly extracted and returned to the Gradio interface.
|
| 5 |
+
|
| 6 |
+
## Root Causes
|
| 7 |
+
|
| 8 |
+
1. **Incomplete Result Extraction**: The `_execute_graph` method in `graph_orchestrator.py` only checked the last executed node (`current_node_id`) for the final result. If the graph execution broke early due to budget/time limits, or if the last node wasn't the synthesizer/writer exit node, the result wouldn't be found.
|
| 9 |
+
|
| 10 |
+
2. **Incomplete Dict Handling**: When the synthesizer or writer nodes returned a dict with `{"message": final_report, "file": file_path}`, the code only extracted the message if the dict had a "file" key. If the dict had a "message" key but no "file" key, the message wouldn't be extracted.
|
| 11 |
+
|
| 12 |
+
3. **No Fallback Logic**: There was no fallback to check all exit nodes for results if the current node wasn't an exit node.
|
| 13 |
+
|
| 14 |
+
## Solution
|
| 15 |
+
|
| 16 |
+
### Changes Made to `src/orchestrator/graph_orchestrator.py`
|
| 17 |
+
|
| 18 |
+
1. **Enhanced Result Extraction** (lines 555-600):
|
| 19 |
+
- First checks if `current_node_id` is an exit node and gets its result
|
| 20 |
+
- If no result, prioritizes checking "synthesizer" and "writer" exit nodes
|
| 21 |
+
- Falls back to checking all exit nodes if still no result
|
| 22 |
+
- Added comprehensive logging to help debug result extraction
|
| 23 |
+
|
| 24 |
+
2. **Improved Dict Handling** (lines 602-640):
|
| 25 |
+
- Now checks for "message" key first (most important)
|
| 26 |
+
- Extracts message from dict even if "file" key is missing
|
| 27 |
+
- Only uses default messages if "message" key is not present
|
| 28 |
+
- Added logging for result type and extraction process
|
| 29 |
+
|
| 30 |
+
3. **Better Error Handling**:
|
| 31 |
+
- Logs warnings when no result is found, including all available node results
|
| 32 |
+
- Logs unexpected result types for debugging
|
| 33 |
+
|
| 34 |
+
## Key Code Changes
|
| 35 |
+
|
| 36 |
+
### Before:
|
| 37 |
+
```python
|
| 38 |
+
final_result = context.get_node_result(current_node_id) if current_node_id else None
|
| 39 |
+
message: str = "Research completed"
|
| 40 |
+
|
| 41 |
+
if isinstance(final_result, str):
|
| 42 |
+
message = final_result
|
| 43 |
+
elif isinstance(final_result, dict):
|
| 44 |
+
if "file" in final_result:
|
| 45 |
+
# Only extracts message if file exists
|
| 46 |
+
message = final_result.get("message", "Report generated. Download available.")
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
### After:
|
| 50 |
+
```python
|
| 51 |
+
# Check all exit nodes with priority
|
| 52 |
+
final_result = None
|
| 53 |
+
if current_node_id and current_node_id in self._graph.exit_nodes:
|
| 54 |
+
final_result = context.get_node_result(current_node_id)
|
| 55 |
+
|
| 56 |
+
if not final_result:
|
| 57 |
+
# Prioritize synthesizer/writer nodes
|
| 58 |
+
for exit_node_id in ["synthesizer", "writer"]:
|
| 59 |
+
if exit_node_id in self._graph.exit_nodes:
|
| 60 |
+
result = context.get_node_result(exit_node_id)
|
| 61 |
+
if result:
|
| 62 |
+
final_result = result
|
| 63 |
+
break
|
| 64 |
+
|
| 65 |
+
# Extract message from dict first
|
| 66 |
+
if isinstance(final_result, dict):
|
| 67 |
+
if "message" in final_result:
|
| 68 |
+
message = final_result["message"] # Extract message regardless of file
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
## Testing Recommendations
|
| 72 |
+
|
| 73 |
+
1. **Test Deep Research Flow**:
|
| 74 |
+
- Run a query that triggers deep research mode
|
| 75 |
+
- Verify the full report is returned, not just "Research completed"
|
| 76 |
+
- Check that reports are properly displayed in the UI
|
| 77 |
+
|
| 78 |
+
2. **Test Iterative Research Flow**:
|
| 79 |
+
- Run a query that triggers iterative research mode
|
| 80 |
+
- Verify the report is returned correctly
|
| 81 |
+
|
| 82 |
+
3. **Test Budget/Time Limits**:
|
| 83 |
+
- Run queries that exceed budget or time limits
|
| 84 |
+
- Verify that partial results are still returned if available
|
| 85 |
+
|
| 86 |
+
4. **Test File Saving**:
|
| 87 |
+
- Verify reports are saved to files
|
| 88 |
+
- Verify file paths are included in event data when available
|
| 89 |
+
|
| 90 |
+
## Files Modified
|
| 91 |
+
|
| 92 |
+
- `src/orchestrator/graph_orchestrator.py`: Enhanced result extraction and message handling logic
|
| 93 |
+
|
| 94 |
+
## Expected Behavior After Fix
|
| 95 |
+
|
| 96 |
+
- Users will see the full research report content in the chat interface
|
| 97 |
+
- Reports will be properly extracted from synthesizer/writer nodes
|
| 98 |
+
- File paths will be included in event data when reports are saved
|
| 99 |
+
- Better logging will help debug any future issues with result extraction
|
| 100 |
+
|
MULTIMODAL_SETTINGS_IMPLEMENTATION_PLAN.md
CHANGED
|
@@ -381,3 +381,4 @@ result["content"] = f"{content}\n\n{file_links}"
|
|
| 381 |
- ✅ No regressions in existing functionality
|
| 382 |
|
| 383 |
|
|
|
|
|
|
| 381 |
- ✅ No regressions in existing functionality
|
| 382 |
|
| 383 |
|
| 384 |
+
|
MULTIMODAL_SETTINGS_IMPLEMENTATION_SUMMARY.md
CHANGED
|
@@ -152,3 +152,4 @@
|
|
| 152 |
5. **Error Handling**: Add better error messages for failed file operations
|
| 153 |
|
| 154 |
|
|
|
|
|
|
| 152 |
5. **Error Handling**: Add better error messages for failed file operations
|
| 153 |
|
| 154 |
|
| 155 |
+
|
README.md
CHANGED
|
@@ -43,6 +43,7 @@ tags:
|
|
| 43 |
[](https://github.com/DeepCritical/GradioDemo)
|
| 44 |
[](deepcritical.github.io/GradioDemo/)
|
| 45 |
[](https://huggingface.co/spaces/DataQuests/DeepCritical)
|
|
|
|
| 46 |
[](https://codecov.io/gh/DeepCritical/GradioDemo)
|
| 47 |
[](https://discord.gg/qdfnvSPcqP)
|
| 48 |
|
|
@@ -55,17 +56,46 @@ tags:
|
|
| 55 |
|
| 56 |
The DETERMINATOR is a powerful generalist deep research agent system that stops at nothing until finding precise answers to complex questions. It uses iterative search-and-judge loops to comprehensively investigate any research question from any domain.
|
| 57 |
|
| 58 |
-
**Key Features**:
|
| 59 |
-
- **Generalist**: Handles queries from any domain (medical, technical, business, scientific, etc.)
|
| 60 |
-
- **Automatic Medical Detection**: Automatically determines if medical knowledge sources (PubMed, ClinicalTrials.gov) are needed
|
| 61 |
-
- **Multi-Source Search**: Web search, PubMed, ClinicalTrials.gov, Europe PMC, RAG
|
| 62 |
-
- **Stops at Nothing**: Only stops at configured limits (budget, time, iterations), otherwise continues until finding precise answers
|
| 63 |
-
- **Evidence Synthesis**: Comprehensive reports with proper citations
|
| 64 |
|
| 65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
|
| 67 |
-
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
> [!IMPORTANT]
|
| 71 |
> **IF YOU ARE A JUDGE**
|
|
@@ -76,18 +106,15 @@ For this hackathon we're proposing a simple yet powerful Deep Research Agent tha
|
|
| 76 |
> - 📖 **Complete README**: Check out the [full README](.github/README.md) for setup, configuration, and contribution guidelines
|
| 77 |
> - 🏆 **Hackathon Submission**: Keep reading below for more information about our MCP Hackathon submission
|
| 78 |
|
| 79 |
-
## Deep Critical In the Medial
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
-
|
| 90 |
-
-
|
| 91 |
|
| 92 |
## Important information
|
| 93 |
|
|
@@ -102,7 +129,7 @@ hf: https://huggingface.co/ViratChauhan
|
|
| 102 |
- [] Apply Deep Research Systems To Generate Short Form Video (up to 5 minutes)
|
| 103 |
- [] Visualize Pydantic Graphs as Loading Screens in the UI
|
| 104 |
- [] Improve Data Science with more Complex Graph Agents
|
| 105 |
-
- [] Create
|
| 106 |
- [] Create Deep Critical Literal Review
|
| 107 |
- [] Create Deep Critical Hypothesis Generator
|
| 108 |
- [] Create PyPi Package
|
|
@@ -134,6 +161,8 @@ hf: https://huggingface.co/ViratChauhan
|
|
| 134 |
- 𝕏 [X](https://x.com/viratzzs/)
|
| 135 |
- 💼 [LinkedIn](https://www.linkedin.com/in/viratchauhan/)
|
| 136 |
- 🤗 [HuggingFace](https://huggingface.co/ViratChauhan)
|
|
|
|
|
|
|
| 137 |
|
| 138 |
|
| 139 |
## Acknowledgements
|
|
|
|
| 43 |
[](https://github.com/DeepCritical/GradioDemo)
|
| 44 |
[](deepcritical.github.io/GradioDemo/)
|
| 45 |
[](https://huggingface.co/spaces/DataQuests/DeepCritical)
|
| 46 |
+
[](https://www.youtube.com/watch?v=PLACEHOLDER)
|
| 47 |
[](https://codecov.io/gh/DeepCritical/GradioDemo)
|
| 48 |
[](https://discord.gg/qdfnvSPcqP)
|
| 49 |
|
|
|
|
| 56 |
|
| 57 |
The DETERMINATOR is a powerful generalist deep research agent system that stops at nothing until finding precise answers to complex questions. It uses iterative search-and-judge loops to comprehensively investigate any research question from any domain.
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
+
> For this hackathon we're proposing a simple yet powerful Deep Research Agent that iteratively looks for the answer until it finds it using general purpose websearch and special purpose retrievers for technical retrievers.
|
| 61 |
+
|
| 62 |
+
## Who We Are & Motivation
|
| 63 |
+
|
| 64 |
+
We're a group from the `DeepCritical` Group that met in the `hugging-science` discord.
|
| 65 |
+
|
| 66 |
+
We're enthusiastic about strongly typed and robust pythonic agentic frameworks , currently building ai-assisted multi-agent systems for research automations , like critical literature reviews , clinical data retrival , and bio informatics and computational medicine applications .
|
| 67 |
|
| 68 |
+
Starting from Magentic Design Patterns for agentic systems , we discovered we could get better results with iterative graphs , orchestrators and planners with magentic agentics as single tools inside iterations.
|
| 69 |
|
| 70 |
+
## Do You Like This App ?
|
| 71 |
+
|
| 72 |
+
Please join us @ https://hf.co/spaces/DataQuests/DeepCritical where we will keep maintaining it !
|
| 73 |
+
|
| 74 |
+
## The DETERMINATOR is Lightweight and POWERFUL
|
| 75 |
+
|
| 76 |
+
- very accessible (multimodal inputs , audio and text out)
|
| 77 |
+
- fully local embeddings
|
| 78 |
+
- configurable providers (local/hosted) for websearch
|
| 79 |
+
- all data stays local
|
| 80 |
+
- fully configurable models and huggingface providers with login
|
| 81 |
+
- easily extensible and hackable
|
| 82 |
+
- uses Gradio a lot (clients, mcp , third party huggingface tools)
|
| 83 |
+
- Modal for text-to-speech
|
| 84 |
+
- Braxel for statistical analysis
|
| 85 |
+
- Open Source Models from around the 🌐World
|
| 86 |
+
- 💖 made with love
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
## Deep Critical In the Media
|
| 90 |
+
|
| 91 |
+
- Social Medial Posts about Deep Critical :
|
| 92 |
+
- 𝕏 []
|
| 93 |
+
- 💼 []
|
| 94 |
+
- 𝕏 []
|
| 95 |
+
|
| 96 |
+
-💼 [
|
| 97 |
+
-
|
| 98 |
+
-
|
| 99 |
|
| 100 |
> [!IMPORTANT]
|
| 101 |
> **IF YOU ARE A JUDGE**
|
|
|
|
| 106 |
> - 📖 **Complete README**: Check out the [full README](.github/README.md) for setup, configuration, and contribution guidelines
|
| 107 |
> - 🏆 **Hackathon Submission**: Keep reading below for more information about our MCP Hackathon submission
|
| 108 |
|
|
|
|
| 109 |
|
| 110 |
+
**Key Features**:
|
| 111 |
+
- **Generalist**: Handles queries from any domain (medical, technical, business, scientific, etc.)
|
| 112 |
+
- **Automatic Medical Detection**: Automatically determines if medical knowledge sources (PubMed, ClinicalTrials.gov) are needed
|
| 113 |
+
- **Multi-Source Search**: Web search, PubMed, ClinicalTrials.gov, Europe PMC, RAG
|
| 114 |
+
- **Stops at Nothing**: Only stops at configured limits (budget, time, iterations), otherwise continues until finding precise answers
|
| 115 |
+
- **Evidence Synthesis**: Comprehensive reports with proper citations
|
| 116 |
+
|
| 117 |
+
**Important**: The DETERMINATOR is a research tool that synthesizes evidence. It cannot provide medical advice or answer medical questions directly.
|
|
|
|
|
|
|
| 118 |
|
| 119 |
## Important information
|
| 120 |
|
|
|
|
| 129 |
- [] Apply Deep Research Systems To Generate Short Form Video (up to 5 minutes)
|
| 130 |
- [] Visualize Pydantic Graphs as Loading Screens in the UI
|
| 131 |
- [] Improve Data Science with more Complex Graph Agents
|
| 132 |
+
- [] Create Deep Critical Drug Reporposing / Discovery Demo
|
| 133 |
- [] Create Deep Critical Literal Review
|
| 134 |
- [] Create Deep Critical Hypothesis Generator
|
| 135 |
- [] Create PyPi Package
|
|
|
|
| 161 |
- 𝕏 [X](https://x.com/viratzzs/)
|
| 162 |
- 💼 [LinkedIn](https://www.linkedin.com/in/viratchauhan/)
|
| 163 |
- 🤗 [HuggingFace](https://huggingface.co/ViratChauhan)
|
| 164 |
+
- **Anna Bossler**
|
| 165 |
+
- 💼 [LinkedIn](https://www.linkedin.com/in/ana-bossler-07304717)
|
| 166 |
|
| 167 |
|
| 168 |
## Acknowledgements
|
REPORT_WRITING_AGENTS_ANALYSIS.md
CHANGED
|
@@ -184,3 +184,4 @@ The infrastructure to handle file outputs in Gradio is in place, but the agents
|
|
| 184 |
|
| 185 |
|
| 186 |
|
|
|
|
|
|
| 184 |
|
| 185 |
|
| 186 |
|
| 187 |
+
|
SERPER_WEBSEARCH_IMPLEMENTATION_PLAN.md
CHANGED
|
@@ -398,3 +398,4 @@ This plan details the implementation of SERPER-based web search by vendoring cod
|
|
| 398 |
|
| 399 |
|
| 400 |
|
|
|
|
|
|
| 398 |
|
| 399 |
|
| 400 |
|
| 401 |
+
|
dev/__init__.py
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
"""Development utilities and plugins."""
|
|
|
|
|
|
| 1 |
"""Development utilities and plugins."""
|
| 2 |
+
|
docs/api/agents.md
CHANGED
|
@@ -270,3 +270,4 @@ def create_input_parser_agent(model: Any | None = None) -> InputParserAgent
|
|
| 270 |
|
| 271 |
|
| 272 |
|
|
|
|
|
|
| 270 |
|
| 271 |
|
| 272 |
|
| 273 |
+
|
docs/api/models.md
CHANGED
|
@@ -248,3 +248,4 @@ class BudgetStatus(BaseModel):
|
|
| 248 |
|
| 249 |
|
| 250 |
|
|
|
|
|
|
| 248 |
|
| 249 |
|
| 250 |
|
| 251 |
+
|
docs/api/services.md
CHANGED
|
@@ -205,5 +205,6 @@ Analyzes a hypothesis using statistical methods.
|
|
| 205 |
|
| 206 |
|
| 207 |
|
|
|
|
| 208 |
|
| 209 |
|
|
|
|
| 205 |
|
| 206 |
|
| 207 |
|
| 208 |
+
|
| 209 |
|
| 210 |
|
docs/api/tools.md
CHANGED
|
@@ -235,3 +235,4 @@ Searches multiple tools in parallel.
|
|
| 235 |
|
| 236 |
|
| 237 |
|
|
|
|
|
|
| 235 |
|
| 236 |
|
| 237 |
|
| 238 |
+
|
docs/architecture/agents.md
CHANGED
|
@@ -192,3 +192,4 @@ Factory functions:
|
|
| 192 |
|
| 193 |
|
| 194 |
|
|
|
|
|
|
| 192 |
|
| 193 |
|
| 194 |
|
| 195 |
+
|
docs/contributing/code-quality.md
CHANGED
|
@@ -81,3 +81,4 @@ async def search(self, query: str, max_results: int = 10) -> list[Evidence]:
|
|
| 81 |
|
| 82 |
|
| 83 |
|
|
|
|
|
|
| 81 |
|
| 82 |
|
| 83 |
|
| 84 |
+
|
docs/contributing/code-style.md
CHANGED
|
@@ -61,3 +61,4 @@ result = await loop.run_in_executor(None, cpu_bound_function, args)
|
|
| 61 |
|
| 62 |
|
| 63 |
|
|
|
|
|
|
| 61 |
|
| 62 |
|
| 63 |
|
| 64 |
+
|
docs/contributing/error-handling.md
CHANGED
|
@@ -69,3 +69,4 @@ except httpx.HTTPError as e:
|
|
| 69 |
|
| 70 |
|
| 71 |
|
|
|
|
|
|
| 69 |
|
| 70 |
|
| 71 |
|
| 72 |
+
|
docs/contributing/implementation-patterns.md
CHANGED
|
@@ -84,3 +84,4 @@ def get_embedding_service() -> EmbeddingService:
|
|
| 84 |
|
| 85 |
|
| 86 |
|
|
|
|
|
|
| 84 |
|
| 85 |
|
| 86 |
|
| 87 |
+
|
docs/contributing/index.md
CHANGED
|
@@ -163,3 +163,4 @@ Thank you for contributing to DeepCritical!
|
|
| 163 |
|
| 164 |
|
| 165 |
|
|
|
|
|
|
| 163 |
|
| 164 |
|
| 165 |
|
| 166 |
+
|
docs/contributing/prompt-engineering.md
CHANGED
|
@@ -69,3 +69,4 @@ This document outlines prompt engineering guidelines and citation validation rul
|
|
| 69 |
|
| 70 |
|
| 71 |
|
|
|
|
|
|
| 69 |
|
| 70 |
|
| 71 |
|
| 72 |
+
|
docs/contributing/testing.md
CHANGED
|
@@ -73,3 +73,4 @@ async def test_real_pubmed_search():
|
|
| 73 |
=======
|
| 74 |
>>>>>>> Stashed changes
|
| 75 |
|
|
|
|
|
|
| 73 |
=======
|
| 74 |
>>>>>>> Stashed changes
|
| 75 |
|
| 76 |
+
|
docs/getting-started/installation.md
CHANGED
|
@@ -156,3 +156,4 @@ uv run pre-commit install
|
|
| 156 |
=======
|
| 157 |
>>>>>>> Stashed changes
|
| 158 |
|
|
|
|
|
|
| 156 |
=======
|
| 157 |
>>>>>>> Stashed changes
|
| 158 |
|
| 159 |
+
|
docs/implementation/IMPLEMENTATION_SUMMARY.md
CHANGED
|
@@ -183,3 +183,4 @@ Located in `src/app.py` lines 667-712:
|
|
| 183 |
|
| 184 |
|
| 185 |
|
|
|
|
|
|
| 183 |
|
| 184 |
|
| 185 |
|
| 186 |
+
|
docs/implementation/TTS_MODAL_IMPLEMENTATION.md
CHANGED
|
@@ -137,3 +137,4 @@ To test TTS:
|
|
| 137 |
|
| 138 |
|
| 139 |
|
|
|
|
|
|
| 137 |
|
| 138 |
|
| 139 |
|
| 140 |
+
|
docs/license.md
CHANGED
|
@@ -39,3 +39,4 @@ SOFTWARE.
|
|
| 39 |
|
| 40 |
|
| 41 |
|
|
|
|
|
|
| 39 |
|
| 40 |
|
| 41 |
|
| 42 |
+
|
docs/team.md
CHANGED
|
@@ -44,3 +44,4 @@ We welcome contributions! See the [Contributing Guide](contributing/index.md) fo
|
|
| 44 |
|
| 45 |
|
| 46 |
|
|
|
|
|
|
| 44 |
|
| 45 |
|
| 46 |
|
| 47 |
+
|
new_env.txt
CHANGED
|
@@ -99,3 +99,4 @@ MODAL_TOKEN_SECRET=your_modal_token_secret_here
|
|
| 99 |
|
| 100 |
|
| 101 |
|
|
|
|
|
|
| 99 |
|
| 100 |
|
| 101 |
|
| 102 |
+
|
src/app.py
CHANGED
|
@@ -949,31 +949,35 @@ def create_demo() -> gr.Blocks:
|
|
| 949 |
),
|
| 950 |
examples=[
|
| 951 |
# When additional_inputs are provided, examples must be lists of lists
|
| 952 |
-
# Each inner list: [message, mode, hf_model, hf_provider]
|
| 953 |
# Using actual model IDs and provider names from inference_models.py
|
| 954 |
# Note: Provider is optional - if empty, HF will auto-select
|
| 955 |
# These examples will NOT run at startup - users must click them after logging in
|
|
|
|
| 956 |
[
|
| 957 |
-
|
| 958 |
-
"
|
| 959 |
-
"
|
| 960 |
-
"",
|
| 961 |
-
"
|
|
|
|
| 962 |
True,
|
| 963 |
],
|
| 964 |
[
|
| 965 |
-
|
| 966 |
-
"
|
| 967 |
-
"
|
|
|
|
| 968 |
"",
|
| 969 |
-
"
|
| 970 |
True,
|
| 971 |
],
|
| 972 |
[
|
| 973 |
-
|
|
|
|
| 974 |
"deep",
|
| 975 |
-
"
|
| 976 |
-
"
|
| 977 |
"deep",
|
| 978 |
True,
|
| 979 |
],
|
|
|
|
| 949 |
),
|
| 950 |
examples=[
|
| 951 |
# When additional_inputs are provided, examples must be lists of lists
|
| 952 |
+
# Each inner list: [message, mode, hf_model, hf_provider, graph_mode, multimodal_enabled]
|
| 953 |
# Using actual model IDs and provider names from inference_models.py
|
| 954 |
# Note: Provider is optional - if empty, HF will auto-select
|
| 955 |
# These examples will NOT run at startup - users must click them after logging in
|
| 956 |
+
# All examples require deep iterative search and information retrieval across multiple sources
|
| 957 |
[
|
| 958 |
+
# Medical research example (only one medical example)
|
| 959 |
+
"Create a comprehensive report on Long COVID treatments including clinical trials, mechanisms, and safety.",
|
| 960 |
+
"deep",
|
| 961 |
+
"zai-org/GLM-4.5-Air",
|
| 962 |
+
"nebius",
|
| 963 |
+
"deep",
|
| 964 |
True,
|
| 965 |
],
|
| 966 |
[
|
| 967 |
+
# Technical/Engineering example requiring deep research
|
| 968 |
+
"Analyze the current state of quantum computing architectures: compare different qubit technologies, error correction methods, and scalability challenges across major platforms including IBM, Google, and IonQ.",
|
| 969 |
+
"deep",
|
| 970 |
+
"Qwen/Qwen3-Next-80B-A3B-Thinking",
|
| 971 |
"",
|
| 972 |
+
"deep",
|
| 973 |
True,
|
| 974 |
],
|
| 975 |
[
|
| 976 |
+
# Business/Scientific example requiring iterative search
|
| 977 |
+
"Investigate the economic and environmental impact of renewable energy transition: analyze cost trends, grid integration challenges, policy frameworks, and market dynamics across solar, wind, and battery storage technologies, in china",
|
| 978 |
"deep",
|
| 979 |
+
"Qwen/Qwen3-235B-A22B-Instruct-2507",
|
| 980 |
+
"",
|
| 981 |
"deep",
|
| 982 |
True,
|
| 983 |
],
|
src/middleware/state_machine.py
CHANGED
|
@@ -127,3 +127,4 @@ def get_workflow_state() -> WorkflowState:
|
|
| 127 |
logger.debug("Workflow state not found, auto-initializing")
|
| 128 |
return init_workflow_state()
|
| 129 |
return state
|
|
|
|
|
|
| 127 |
logger.debug("Workflow state not found, auto-initializing")
|
| 128 |
return init_workflow_state()
|
| 129 |
return state
|
| 130 |
+
|
src/orchestrator/graph_orchestrator.py
CHANGED
|
@@ -552,8 +552,57 @@ class GraphOrchestrator:
|
|
| 552 |
|
| 553 |
current_node_id = next_nodes[0] # For now, take first next node (handle parallel later)
|
| 554 |
|
| 555 |
-
# Final event - get result from
|
| 556 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 557 |
|
| 558 |
# Check if final result contains file information
|
| 559 |
event_data: dict[str, Any] = {"mode": self.mode, "iterations": iteration}
|
|
@@ -561,21 +610,45 @@ class GraphOrchestrator:
|
|
| 561 |
|
| 562 |
if isinstance(final_result, str):
|
| 563 |
message = final_result
|
|
|
|
| 564 |
elif isinstance(final_result, dict):
|
| 565 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 566 |
if "file" in final_result:
|
| 567 |
file_path = final_result["file"]
|
| 568 |
if isinstance(file_path, str):
|
| 569 |
event_data["file"] = file_path
|
| 570 |
-
|
|
|
|
|
|
|
|
|
|
| 571 |
elif "files" in final_result:
|
| 572 |
files = final_result["files"]
|
| 573 |
if isinstance(files, list):
|
| 574 |
event_data["files"] = files
|
| 575 |
-
|
|
|
|
|
|
|
| 576 |
elif isinstance(files, str):
|
| 577 |
event_data["files"] = [files]
|
| 578 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 579 |
|
| 580 |
yield AgentEvent(
|
| 581 |
type="complete",
|
|
|
|
| 552 |
|
| 553 |
current_node_id = next_nodes[0] # For now, take first next node (handle parallel later)
|
| 554 |
|
| 555 |
+
# Final event - get result from exit nodes (prioritize synthesizer/writer nodes)
|
| 556 |
+
# First try to get result from current node (if it's an exit node)
|
| 557 |
+
final_result = None
|
| 558 |
+
if current_node_id and current_node_id in self._graph.exit_nodes:
|
| 559 |
+
final_result = context.get_node_result(current_node_id)
|
| 560 |
+
self.logger.debug(
|
| 561 |
+
"Final result from current exit node",
|
| 562 |
+
node_id=current_node_id,
|
| 563 |
+
has_result=final_result is not None,
|
| 564 |
+
result_type=type(final_result).__name__ if final_result else None,
|
| 565 |
+
)
|
| 566 |
+
|
| 567 |
+
# If no result from current node, check all exit nodes for results
|
| 568 |
+
# Prioritize synthesizer (deep research) or writer (iterative research)
|
| 569 |
+
if not final_result:
|
| 570 |
+
exit_node_priority = ["synthesizer", "writer"]
|
| 571 |
+
for exit_node_id in exit_node_priority:
|
| 572 |
+
if exit_node_id in self._graph.exit_nodes:
|
| 573 |
+
result = context.get_node_result(exit_node_id)
|
| 574 |
+
if result:
|
| 575 |
+
final_result = result
|
| 576 |
+
current_node_id = exit_node_id
|
| 577 |
+
self.logger.debug(
|
| 578 |
+
"Final result from priority exit node",
|
| 579 |
+
node_id=exit_node_id,
|
| 580 |
+
result_type=type(final_result).__name__,
|
| 581 |
+
)
|
| 582 |
+
break
|
| 583 |
+
|
| 584 |
+
# If still no result, check all exit nodes
|
| 585 |
+
if not final_result:
|
| 586 |
+
for exit_node_id in self._graph.exit_nodes:
|
| 587 |
+
result = context.get_node_result(exit_node_id)
|
| 588 |
+
if result:
|
| 589 |
+
final_result = result
|
| 590 |
+
current_node_id = exit_node_id
|
| 591 |
+
self.logger.debug(
|
| 592 |
+
"Final result from any exit node",
|
| 593 |
+
node_id=exit_node_id,
|
| 594 |
+
result_type=type(final_result).__name__,
|
| 595 |
+
)
|
| 596 |
+
break
|
| 597 |
+
|
| 598 |
+
# Log warning if no result found
|
| 599 |
+
if not final_result:
|
| 600 |
+
self.logger.warning(
|
| 601 |
+
"No final result found in exit nodes",
|
| 602 |
+
exit_nodes=list(self._graph.exit_nodes),
|
| 603 |
+
visited_nodes=list(context.visited_nodes),
|
| 604 |
+
all_node_results=list(context.node_results.keys()),
|
| 605 |
+
)
|
| 606 |
|
| 607 |
# Check if final result contains file information
|
| 608 |
event_data: dict[str, Any] = {"mode": self.mode, "iterations": iteration}
|
|
|
|
| 610 |
|
| 611 |
if isinstance(final_result, str):
|
| 612 |
message = final_result
|
| 613 |
+
self.logger.debug("Final message extracted from string result", length=len(message))
|
| 614 |
elif isinstance(final_result, dict):
|
| 615 |
+
# First check for message key (most important)
|
| 616 |
+
if "message" in final_result:
|
| 617 |
+
message = final_result["message"]
|
| 618 |
+
self.logger.debug(
|
| 619 |
+
"Final message extracted from dict 'message' key",
|
| 620 |
+
length=len(message) if isinstance(message, str) else 0,
|
| 621 |
+
)
|
| 622 |
+
|
| 623 |
+
# Then check for file paths
|
| 624 |
if "file" in final_result:
|
| 625 |
file_path = final_result["file"]
|
| 626 |
if isinstance(file_path, str):
|
| 627 |
event_data["file"] = file_path
|
| 628 |
+
# Only override message if not already set from "message" key
|
| 629 |
+
if "message" not in final_result:
|
| 630 |
+
message = "Report generated. Download available."
|
| 631 |
+
self.logger.debug("File path added to event data", file_path=file_path)
|
| 632 |
elif "files" in final_result:
|
| 633 |
files = final_result["files"]
|
| 634 |
if isinstance(files, list):
|
| 635 |
event_data["files"] = files
|
| 636 |
+
# Only override message if not already set from "message" key
|
| 637 |
+
if "message" not in final_result:
|
| 638 |
+
message = "Report generated. Downloads available."
|
| 639 |
elif isinstance(files, str):
|
| 640 |
event_data["files"] = [files]
|
| 641 |
+
# Only override message if not already set from "message" key
|
| 642 |
+
if "message" not in final_result:
|
| 643 |
+
message = "Report generated. Download available."
|
| 644 |
+
self.logger.debug("File paths added to event data", count=len(event_data.get("files", [])))
|
| 645 |
+
else:
|
| 646 |
+
# Log warning if result type is unexpected
|
| 647 |
+
self.logger.warning(
|
| 648 |
+
"Final result has unexpected type",
|
| 649 |
+
result_type=type(final_result).__name__ if final_result else None,
|
| 650 |
+
result_repr=str(final_result)[:200] if final_result else None,
|
| 651 |
+
)
|
| 652 |
|
| 653 |
yield AgentEvent(
|
| 654 |
type="complete",
|
src/services/report_file_service.py
CHANGED
|
@@ -272,3 +272,4 @@ def get_report_file_service() -> ReportFileService:
|
|
| 272 |
return ReportFileService()
|
| 273 |
|
| 274 |
return _get_service()
|
|
|
|
|
|
| 272 |
return ReportFileService()
|
| 273 |
|
| 274 |
return _get_service()
|
| 275 |
+
|
src/tools/searchxng_web_search.py
CHANGED
|
@@ -113,3 +113,4 @@ class SearchXNGWebSearchTool:
|
|
| 113 |
except Exception as e:
|
| 114 |
logger.error("Unexpected error in SearchXNG search", error=str(e), query=final_query)
|
| 115 |
raise SearchError(f"SearchXNG search failed: {e}") from e
|
|
|
|
|
|
| 113 |
except Exception as e:
|
| 114 |
logger.error("Unexpected error in SearchXNG search", error=str(e), query=final_query)
|
| 115 |
raise SearchError(f"SearchXNG search failed: {e}") from e
|
| 116 |
+
|
src/tools/serper_web_search.py
CHANGED
|
@@ -113,3 +113,4 @@ class SerperWebSearchTool:
|
|
| 113 |
except Exception as e:
|
| 114 |
logger.error("Unexpected error in Serper search", error=str(e), query=final_query)
|
| 115 |
raise SearchError(f"Serper search failed: {e}") from e
|
|
|
|
|
|
| 113 |
except Exception as e:
|
| 114 |
logger.error("Unexpected error in Serper search", error=str(e), query=final_query)
|
| 115 |
raise SearchError(f"Serper search failed: {e}") from e
|
| 116 |
+
|
src/tools/vendored/crawl_website.py
CHANGED
|
@@ -125,3 +125,4 @@ async def crawl_website(starting_url: str) -> list[ScrapeResult] | str:
|
|
| 125 |
# Use scrape_urls to get the content for all discovered pages
|
| 126 |
result = await scrape_urls(pages_to_scrape_snippets)
|
| 127 |
return result
|
|
|
|
|
|
| 125 |
# Use scrape_urls to get the content for all discovered pages
|
| 126 |
result = await scrape_urls(pages_to_scrape_snippets)
|
| 127 |
return result
|
| 128 |
+
|
src/tools/vendored/searchxng_client.py
CHANGED
|
@@ -94,3 +94,4 @@ class SearchXNGClient:
|
|
| 94 |
except Exception as e:
|
| 95 |
logger.error("Unexpected error in SearchXNG search", error=str(e), query=query)
|
| 96 |
raise SearchError(f"SearchXNG search failed: {e}") from e
|
|
|
|
|
|
| 94 |
except Exception as e:
|
| 95 |
logger.error("Unexpected error in SearchXNG search", error=str(e), query=query)
|
| 96 |
raise SearchError(f"SearchXNG search failed: {e}") from e
|
| 97 |
+
|
src/tools/vendored/serper_client.py
CHANGED
|
@@ -90,3 +90,4 @@ class SerperClient:
|
|
| 90 |
except Exception as e:
|
| 91 |
logger.error("Unexpected error in Serper search", error=str(e), query=query)
|
| 92 |
raise SearchError(f"Serper search failed: {e}") from e
|
|
|
|
|
|
| 90 |
except Exception as e:
|
| 91 |
logger.error("Unexpected error in Serper search", error=str(e), query=query)
|
| 92 |
raise SearchError(f"Serper search failed: {e}") from e
|
| 93 |
+
|
src/tools/vendored/web_search_core.py
CHANGED
|
@@ -199,3 +199,4 @@ def is_valid_url(url: str) -> bool:
|
|
| 199 |
if any(ext in url for ext in restricted_extensions):
|
| 200 |
return False
|
| 201 |
return True
|
|
|
|
|
|
| 199 |
if any(ext in url for ext in restricted_extensions):
|
| 200 |
return False
|
| 201 |
return True
|
| 202 |
+
|
src/tools/web_search_factory.py
CHANGED
|
@@ -66,3 +66,4 @@ def create_web_search_tool() -> SearchTool | None:
|
|
| 66 |
except Exception as e:
|
| 67 |
logger.error("Unexpected error creating web search tool", error=str(e), provider=provider)
|
| 68 |
return None
|
|
|
|
|
|
| 66 |
except Exception as e:
|
| 67 |
logger.error("Unexpected error creating web search tool", error=str(e), provider=provider)
|
| 68 |
return None
|
| 69 |
+
|
tests/unit/middleware/test_budget_tracker_phase7.py
CHANGED
|
@@ -176,4 +176,5 @@ class TestIterationTokenTracking:
|
|
| 176 |
|
| 177 |
|
| 178 |
|
|
|
|
| 179 |
|
|
|
|
| 176 |
|
| 177 |
|
| 178 |
|
| 179 |
+
|
| 180 |
|
tests/unit/middleware/test_state_machine.py
CHANGED
|
@@ -373,4 +373,5 @@ class TestContextVarIsolation:
|
|
| 373 |
|
| 374 |
|
| 375 |
|
|
|
|
| 376 |
|
|
|
|
| 373 |
|
| 374 |
|
| 375 |
|
| 376 |
+
|
| 377 |
|
tests/unit/middleware/test_workflow_manager.py
CHANGED
|
@@ -303,4 +303,5 @@ class TestWorkflowManager:
|
|
| 303 |
|
| 304 |
|
| 305 |
|
|
|
|
| 306 |
|
|
|
|
| 303 |
|
| 304 |
|
| 305 |
|
| 306 |
+
|
| 307 |
|