Spaces:
Running
Running
Joseph Pollack
commited on
attempts to solve the api_key issue for huggingface , settings not appearing , set settings for audio , adds modal gpu , speech to text with mic input addon, adds graphs
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- docs/api/agents.md +1 -0
- docs/api/models.md +1 -0
- docs/api/orchestrators.md +1 -0
- docs/api/services.md +1 -0
- docs/api/tools.md +1 -0
- docs/architecture/agents.md +1 -0
- docs/architecture/middleware.md +1 -0
- docs/architecture/services.md +1 -0
- docs/architecture/tools.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/examples.md +1 -0
- docs/getting-started/installation.md +1 -0
- docs/getting-started/mcp-integration.md +1 -0
- docs/getting-started/quick-start.md +1 -0
- docs/implementation/IMPLEMENTATION_SUMMARY.md +1 -0
- docs/implementation/TTS_MODAL_IMPLEMENTATION.md +1 -0
- docs/license.md +1 -0
- docs/overview/architecture.md +1 -0
- docs/overview/features.md +1 -0
- docs/team.md +1 -0
- new_env.txt +1 -0
- src/agent_factory/agents.py +24 -16
- src/agent_factory/judges.py +32 -6
- src/agents/input_parser.py +3 -2
- src/agents/knowledge_gap.py +3 -2
- src/agents/long_writer.py +3 -2
- src/agents/proofreader.py +3 -2
- src/agents/thinking.py +3 -2
- src/agents/tool_selector.py +3 -2
- src/agents/writer.py +3 -2
- src/middleware/state_machine.py +1 -0
- src/orchestrator/graph_orchestrator.py +23 -14
- src/orchestrator/planner_agent.py +3 -2
- src/orchestrator/research_flow.py +13 -7
- src/orchestrator_factory.py +3 -0
- src/services/image_ocr.py +3 -1
- src/services/multimodal_processing.py +1 -0
- src/services/stt_gradio.py +2 -1
- src/tools/crawl_adapter.py +1 -0
- src/tools/web_search_adapter.py +1 -0
- src/utils/config.py +1 -67
- tests/unit/middleware/__init__.py +1 -0
- tests/unit/middleware/test_budget_tracker_phase7.py +1 -0
- tests/unit/middleware/test_state_machine.py +1 -0
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/orchestrators.md
CHANGED
|
@@ -195,3 +195,4 @@ Runs Magentic orchestration.
|
|
| 195 |
|
| 196 |
|
| 197 |
|
|
|
|
|
|
| 195 |
|
| 196 |
|
| 197 |
|
| 198 |
+
|
docs/api/services.md
CHANGED
|
@@ -201,3 +201,4 @@ Analyzes a hypothesis using statistical methods.
|
|
| 201 |
|
| 202 |
|
| 203 |
|
|
|
|
|
|
| 201 |
|
| 202 |
|
| 203 |
|
| 204 |
+
|
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/architecture/middleware.md
CHANGED
|
@@ -142,3 +142,4 @@ All middleware components use `ContextVar` for thread-safe isolation:
|
|
| 142 |
|
| 143 |
|
| 144 |
|
|
|
|
|
|
| 142 |
|
| 143 |
|
| 144 |
|
| 145 |
+
|
docs/architecture/services.md
CHANGED
|
@@ -142,3 +142,4 @@ if settings.has_openai_key:
|
|
| 142 |
|
| 143 |
|
| 144 |
|
|
|
|
|
|
| 142 |
|
| 143 |
|
| 144 |
|
| 145 |
+
|
docs/architecture/tools.md
CHANGED
|
@@ -175,3 +175,4 @@ search_handler = SearchHandler(
|
|
| 175 |
|
| 176 |
|
| 177 |
|
|
|
|
|
|
| 175 |
|
| 176 |
|
| 177 |
|
| 178 |
+
|
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
|
@@ -65,3 +65,4 @@ async def test_real_pubmed_search():
|
|
| 65 |
|
| 66 |
|
| 67 |
|
|
|
|
|
|
| 65 |
|
| 66 |
|
| 67 |
|
| 68 |
+
|
docs/getting-started/examples.md
CHANGED
|
@@ -209,3 +209,4 @@ USE_GRAPH_EXECUTION=true
|
|
| 209 |
|
| 210 |
|
| 211 |
|
|
|
|
|
|
| 209 |
|
| 210 |
|
| 211 |
|
| 212 |
+
|
docs/getting-started/installation.md
CHANGED
|
@@ -148,3 +148,4 @@ uv run pre-commit install
|
|
| 148 |
|
| 149 |
|
| 150 |
|
|
|
|
|
|
| 148 |
|
| 149 |
|
| 150 |
|
| 151 |
+
|
docs/getting-started/mcp-integration.md
CHANGED
|
@@ -215,3 +215,4 @@ You can configure multiple DeepCritical instances:
|
|
| 215 |
|
| 216 |
|
| 217 |
|
|
|
|
|
|
| 215 |
|
| 216 |
|
| 217 |
|
| 218 |
+
|
docs/getting-started/quick-start.md
CHANGED
|
@@ -119,3 +119,4 @@ What are the active clinical trials investigating Alzheimer's disease treatments
|
|
| 119 |
|
| 120 |
|
| 121 |
|
|
|
|
|
|
| 119 |
|
| 120 |
|
| 121 |
|
| 122 |
+
|
docs/implementation/IMPLEMENTATION_SUMMARY.md
CHANGED
|
@@ -178,3 +178,4 @@ Located in `src/app.py` lines 667-712:
|
|
| 178 |
|
| 179 |
|
| 180 |
|
|
|
|
|
|
| 178 |
|
| 179 |
|
| 180 |
|
| 181 |
+
|
docs/implementation/TTS_MODAL_IMPLEMENTATION.md
CHANGED
|
@@ -132,3 +132,4 @@ To test TTS:
|
|
| 132 |
|
| 133 |
|
| 134 |
|
|
|
|
|
|
| 132 |
|
| 133 |
|
| 134 |
|
| 135 |
+
|
docs/license.md
CHANGED
|
@@ -39,3 +39,4 @@ SOFTWARE.
|
|
| 39 |
|
| 40 |
|
| 41 |
|
|
|
|
|
|
| 39 |
|
| 40 |
|
| 41 |
|
| 42 |
+
|
docs/overview/architecture.md
CHANGED
|
@@ -196,3 +196,4 @@ The system supports complex research workflows through:
|
|
| 196 |
|
| 197 |
|
| 198 |
|
|
|
|
|
|
| 196 |
|
| 197 |
|
| 198 |
|
| 199 |
+
|
docs/overview/features.md
CHANGED
|
@@ -148,3 +148,4 @@ DeepCritical provides a comprehensive set of features for AI-assisted research:
|
|
| 148 |
|
| 149 |
|
| 150 |
|
|
|
|
|
|
| 148 |
|
| 149 |
|
| 150 |
|
| 151 |
+
|
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
|
@@ -94,3 +94,4 @@ MODAL_TOKEN_SECRET=your_modal_token_secret_here
|
|
| 94 |
|
| 95 |
|
| 96 |
|
|
|
|
|
|
| 94 |
|
| 95 |
|
| 96 |
|
| 97 |
+
|
src/agent_factory/agents.py
CHANGED
|
@@ -27,12 +27,13 @@ if TYPE_CHECKING:
|
|
| 27 |
logger = structlog.get_logger()
|
| 28 |
|
| 29 |
|
| 30 |
-
def create_input_parser_agent(model: Any | None = None) -> "InputParserAgent":
|
| 31 |
"""
|
| 32 |
Create input parser agent for query analysis and research mode detection.
|
| 33 |
|
| 34 |
Args:
|
| 35 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 36 |
|
| 37 |
Returns:
|
| 38 |
Configured InputParserAgent instance
|
|
@@ -44,18 +45,19 @@ def create_input_parser_agent(model: Any | None = None) -> "InputParserAgent":
|
|
| 44 |
|
| 45 |
try:
|
| 46 |
logger.debug("Creating input parser agent")
|
| 47 |
-
return _create_agent(model=model)
|
| 48 |
except Exception as e:
|
| 49 |
logger.error("Failed to create input parser agent", error=str(e))
|
| 50 |
raise ConfigurationError(f"Failed to create input parser agent: {e}") from e
|
| 51 |
|
| 52 |
|
| 53 |
-
def create_planner_agent(model: Any | None = None) -> "PlannerAgent":
|
| 54 |
"""
|
| 55 |
Create planner agent with web search and crawl tools.
|
| 56 |
|
| 57 |
Args:
|
| 58 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 59 |
|
| 60 |
Returns:
|
| 61 |
Configured PlannerAgent instance
|
|
@@ -68,18 +70,19 @@ def create_planner_agent(model: Any | None = None) -> "PlannerAgent":
|
|
| 68 |
|
| 69 |
try:
|
| 70 |
logger.debug("Creating planner agent")
|
| 71 |
-
return _create_planner_agent(model=model)
|
| 72 |
except Exception as e:
|
| 73 |
logger.error("Failed to create planner agent", error=str(e))
|
| 74 |
raise ConfigurationError(f"Failed to create planner agent: {e}") from e
|
| 75 |
|
| 76 |
|
| 77 |
-
def create_knowledge_gap_agent(model: Any | None = None) -> "KnowledgeGapAgent":
|
| 78 |
"""
|
| 79 |
Create knowledge gap agent for evaluating research completeness.
|
| 80 |
|
| 81 |
Args:
|
| 82 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 83 |
|
| 84 |
Returns:
|
| 85 |
Configured KnowledgeGapAgent instance
|
|
@@ -91,18 +94,19 @@ def create_knowledge_gap_agent(model: Any | None = None) -> "KnowledgeGapAgent":
|
|
| 91 |
|
| 92 |
try:
|
| 93 |
logger.debug("Creating knowledge gap agent")
|
| 94 |
-
return _create_agent(model=model)
|
| 95 |
except Exception as e:
|
| 96 |
logger.error("Failed to create knowledge gap agent", error=str(e))
|
| 97 |
raise ConfigurationError(f"Failed to create knowledge gap agent: {e}") from e
|
| 98 |
|
| 99 |
|
| 100 |
-
def create_tool_selector_agent(model: Any | None = None) -> "ToolSelectorAgent":
|
| 101 |
"""
|
| 102 |
Create tool selector agent for choosing tools to address gaps.
|
| 103 |
|
| 104 |
Args:
|
| 105 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 106 |
|
| 107 |
Returns:
|
| 108 |
Configured ToolSelectorAgent instance
|
|
@@ -114,18 +118,19 @@ def create_tool_selector_agent(model: Any | None = None) -> "ToolSelectorAgent":
|
|
| 114 |
|
| 115 |
try:
|
| 116 |
logger.debug("Creating tool selector agent")
|
| 117 |
-
return _create_agent(model=model)
|
| 118 |
except Exception as e:
|
| 119 |
logger.error("Failed to create tool selector agent", error=str(e))
|
| 120 |
raise ConfigurationError(f"Failed to create tool selector agent: {e}") from e
|
| 121 |
|
| 122 |
|
| 123 |
-
def create_thinking_agent(model: Any | None = None) -> "ThinkingAgent":
|
| 124 |
"""
|
| 125 |
Create thinking agent for generating observations.
|
| 126 |
|
| 127 |
Args:
|
| 128 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 129 |
|
| 130 |
Returns:
|
| 131 |
Configured ThinkingAgent instance
|
|
@@ -137,18 +142,19 @@ def create_thinking_agent(model: Any | None = None) -> "ThinkingAgent":
|
|
| 137 |
|
| 138 |
try:
|
| 139 |
logger.debug("Creating thinking agent")
|
| 140 |
-
return _create_agent(model=model)
|
| 141 |
except Exception as e:
|
| 142 |
logger.error("Failed to create thinking agent", error=str(e))
|
| 143 |
raise ConfigurationError(f"Failed to create thinking agent: {e}") from e
|
| 144 |
|
| 145 |
|
| 146 |
-
def create_writer_agent(model: Any | None = None) -> "WriterAgent":
|
| 147 |
"""
|
| 148 |
Create writer agent for generating final reports.
|
| 149 |
|
| 150 |
Args:
|
| 151 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 152 |
|
| 153 |
Returns:
|
| 154 |
Configured WriterAgent instance
|
|
@@ -160,18 +166,19 @@ def create_writer_agent(model: Any | None = None) -> "WriterAgent":
|
|
| 160 |
|
| 161 |
try:
|
| 162 |
logger.debug("Creating writer agent")
|
| 163 |
-
return _create_agent(model=model)
|
| 164 |
except Exception as e:
|
| 165 |
logger.error("Failed to create writer agent", error=str(e))
|
| 166 |
raise ConfigurationError(f"Failed to create writer agent: {e}") from e
|
| 167 |
|
| 168 |
|
| 169 |
-
def create_long_writer_agent(model: Any | None = None) -> "LongWriterAgent":
|
| 170 |
"""
|
| 171 |
Create long writer agent for iteratively writing report sections.
|
| 172 |
|
| 173 |
Args:
|
| 174 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 175 |
|
| 176 |
Returns:
|
| 177 |
Configured LongWriterAgent instance
|
|
@@ -183,18 +190,19 @@ def create_long_writer_agent(model: Any | None = None) -> "LongWriterAgent":
|
|
| 183 |
|
| 184 |
try:
|
| 185 |
logger.debug("Creating long writer agent")
|
| 186 |
-
return _create_agent(model=model)
|
| 187 |
except Exception as e:
|
| 188 |
logger.error("Failed to create long writer agent", error=str(e))
|
| 189 |
raise ConfigurationError(f"Failed to create long writer agent: {e}") from e
|
| 190 |
|
| 191 |
|
| 192 |
-
def create_proofreader_agent(model: Any | None = None) -> "ProofreaderAgent":
|
| 193 |
"""
|
| 194 |
Create proofreader agent for finalizing report drafts.
|
| 195 |
|
| 196 |
Args:
|
| 197 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 198 |
|
| 199 |
Returns:
|
| 200 |
Configured ProofreaderAgent instance
|
|
@@ -206,7 +214,7 @@ def create_proofreader_agent(model: Any | None = None) -> "ProofreaderAgent":
|
|
| 206 |
|
| 207 |
try:
|
| 208 |
logger.debug("Creating proofreader agent")
|
| 209 |
-
return _create_agent(model=model)
|
| 210 |
except Exception as e:
|
| 211 |
logger.error("Failed to create proofreader agent", error=str(e))
|
| 212 |
raise ConfigurationError(f"Failed to create proofreader agent: {e}") from e
|
|
|
|
| 27 |
logger = structlog.get_logger()
|
| 28 |
|
| 29 |
|
| 30 |
+
def create_input_parser_agent(model: Any | None = None, oauth_token: str | None = None) -> "InputParserAgent":
|
| 31 |
"""
|
| 32 |
Create input parser agent for query analysis and research mode detection.
|
| 33 |
|
| 34 |
Args:
|
| 35 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 36 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 37 |
|
| 38 |
Returns:
|
| 39 |
Configured InputParserAgent instance
|
|
|
|
| 45 |
|
| 46 |
try:
|
| 47 |
logger.debug("Creating input parser agent")
|
| 48 |
+
return _create_agent(model=model, oauth_token=oauth_token)
|
| 49 |
except Exception as e:
|
| 50 |
logger.error("Failed to create input parser agent", error=str(e))
|
| 51 |
raise ConfigurationError(f"Failed to create input parser agent: {e}") from e
|
| 52 |
|
| 53 |
|
| 54 |
+
def create_planner_agent(model: Any | None = None, oauth_token: str | None = None) -> "PlannerAgent":
|
| 55 |
"""
|
| 56 |
Create planner agent with web search and crawl tools.
|
| 57 |
|
| 58 |
Args:
|
| 59 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 60 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 61 |
|
| 62 |
Returns:
|
| 63 |
Configured PlannerAgent instance
|
|
|
|
| 70 |
|
| 71 |
try:
|
| 72 |
logger.debug("Creating planner agent")
|
| 73 |
+
return _create_planner_agent(model=model, oauth_token=oauth_token)
|
| 74 |
except Exception as e:
|
| 75 |
logger.error("Failed to create planner agent", error=str(e))
|
| 76 |
raise ConfigurationError(f"Failed to create planner agent: {e}") from e
|
| 77 |
|
| 78 |
|
| 79 |
+
def create_knowledge_gap_agent(model: Any | None = None, oauth_token: str | None = None) -> "KnowledgeGapAgent":
|
| 80 |
"""
|
| 81 |
Create knowledge gap agent for evaluating research completeness.
|
| 82 |
|
| 83 |
Args:
|
| 84 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 85 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 86 |
|
| 87 |
Returns:
|
| 88 |
Configured KnowledgeGapAgent instance
|
|
|
|
| 94 |
|
| 95 |
try:
|
| 96 |
logger.debug("Creating knowledge gap agent")
|
| 97 |
+
return _create_agent(model=model, oauth_token=oauth_token)
|
| 98 |
except Exception as e:
|
| 99 |
logger.error("Failed to create knowledge gap agent", error=str(e))
|
| 100 |
raise ConfigurationError(f"Failed to create knowledge gap agent: {e}") from e
|
| 101 |
|
| 102 |
|
| 103 |
+
def create_tool_selector_agent(model: Any | None = None, oauth_token: str | None = None) -> "ToolSelectorAgent":
|
| 104 |
"""
|
| 105 |
Create tool selector agent for choosing tools to address gaps.
|
| 106 |
|
| 107 |
Args:
|
| 108 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 109 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 110 |
|
| 111 |
Returns:
|
| 112 |
Configured ToolSelectorAgent instance
|
|
|
|
| 118 |
|
| 119 |
try:
|
| 120 |
logger.debug("Creating tool selector agent")
|
| 121 |
+
return _create_agent(model=model, oauth_token=oauth_token)
|
| 122 |
except Exception as e:
|
| 123 |
logger.error("Failed to create tool selector agent", error=str(e))
|
| 124 |
raise ConfigurationError(f"Failed to create tool selector agent: {e}") from e
|
| 125 |
|
| 126 |
|
| 127 |
+
def create_thinking_agent(model: Any | None = None, oauth_token: str | None = None) -> "ThinkingAgent":
|
| 128 |
"""
|
| 129 |
Create thinking agent for generating observations.
|
| 130 |
|
| 131 |
Args:
|
| 132 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 133 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 134 |
|
| 135 |
Returns:
|
| 136 |
Configured ThinkingAgent instance
|
|
|
|
| 142 |
|
| 143 |
try:
|
| 144 |
logger.debug("Creating thinking agent")
|
| 145 |
+
return _create_agent(model=model, oauth_token=oauth_token)
|
| 146 |
except Exception as e:
|
| 147 |
logger.error("Failed to create thinking agent", error=str(e))
|
| 148 |
raise ConfigurationError(f"Failed to create thinking agent: {e}") from e
|
| 149 |
|
| 150 |
|
| 151 |
+
def create_writer_agent(model: Any | None = None, oauth_token: str | None = None) -> "WriterAgent":
|
| 152 |
"""
|
| 153 |
Create writer agent for generating final reports.
|
| 154 |
|
| 155 |
Args:
|
| 156 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 157 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 158 |
|
| 159 |
Returns:
|
| 160 |
Configured WriterAgent instance
|
|
|
|
| 166 |
|
| 167 |
try:
|
| 168 |
logger.debug("Creating writer agent")
|
| 169 |
+
return _create_agent(model=model, oauth_token=oauth_token)
|
| 170 |
except Exception as e:
|
| 171 |
logger.error("Failed to create writer agent", error=str(e))
|
| 172 |
raise ConfigurationError(f"Failed to create writer agent: {e}") from e
|
| 173 |
|
| 174 |
|
| 175 |
+
def create_long_writer_agent(model: Any | None = None, oauth_token: str | None = None) -> "LongWriterAgent":
|
| 176 |
"""
|
| 177 |
Create long writer agent for iteratively writing report sections.
|
| 178 |
|
| 179 |
Args:
|
| 180 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 181 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 182 |
|
| 183 |
Returns:
|
| 184 |
Configured LongWriterAgent instance
|
|
|
|
| 190 |
|
| 191 |
try:
|
| 192 |
logger.debug("Creating long writer agent")
|
| 193 |
+
return _create_agent(model=model, oauth_token=oauth_token)
|
| 194 |
except Exception as e:
|
| 195 |
logger.error("Failed to create long writer agent", error=str(e))
|
| 196 |
raise ConfigurationError(f"Failed to create long writer agent: {e}") from e
|
| 197 |
|
| 198 |
|
| 199 |
+
def create_proofreader_agent(model: Any | None = None, oauth_token: str | None = None) -> "ProofreaderAgent":
|
| 200 |
"""
|
| 201 |
Create proofreader agent for finalizing report drafts.
|
| 202 |
|
| 203 |
Args:
|
| 204 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 205 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 206 |
|
| 207 |
Returns:
|
| 208 |
Configured ProofreaderAgent instance
|
|
|
|
| 214 |
|
| 215 |
try:
|
| 216 |
logger.debug("Creating proofreader agent")
|
| 217 |
+
return _create_agent(model=model, oauth_token=oauth_token)
|
| 218 |
except Exception as e:
|
| 219 |
logger.error("Failed to create proofreader agent", error=str(e))
|
| 220 |
raise ConfigurationError(f"Failed to create proofreader agent: {e}") from e
|
src/agent_factory/judges.py
CHANGED
|
@@ -32,34 +32,60 @@ def get_model(oauth_token: str | None = None) -> Any:
|
|
| 32 |
Explicitly passes API keys from settings to avoid requiring
|
| 33 |
users to export environment variables manually.
|
| 34 |
|
|
|
|
|
|
|
|
|
|
| 35 |
Args:
|
| 36 |
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 37 |
"""
|
| 38 |
-
llm_provider = settings.llm_provider
|
| 39 |
-
|
| 40 |
# Priority: oauth_token > env vars
|
| 41 |
effective_hf_token = oauth_token or settings.hf_token or settings.huggingface_api_key
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
if llm_provider == "anthropic":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
provider = AnthropicProvider(api_key=settings.anthropic_api_key)
|
| 45 |
return AnthropicModel(settings.anthropic_model, provider=provider)
|
| 46 |
|
| 47 |
if llm_provider == "huggingface":
|
| 48 |
-
#
|
| 49 |
model_name = settings.huggingface_model or "meta-llama/Llama-3.1-8B-Instruct"
|
| 50 |
-
hf_provider = HuggingFaceProvider(api_key=
|
| 51 |
return HuggingFaceModel(model_name, provider=hf_provider)
|
| 52 |
|
| 53 |
if llm_provider == "openai":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
openai_provider = OpenAIProvider(api_key=settings.openai_api_key)
|
| 55 |
return OpenAIModel(settings.openai_model, provider=openai_provider)
|
| 56 |
|
| 57 |
# Default to HuggingFace if provider is unknown or not specified
|
| 58 |
-
if llm_provider
|
| 59 |
logger.warning("Unknown LLM provider, defaulting to HuggingFace", provider=llm_provider)
|
| 60 |
|
| 61 |
model_name = settings.huggingface_model or "meta-llama/Llama-3.1-8B-Instruct"
|
| 62 |
-
hf_provider = HuggingFaceProvider(api_key=
|
| 63 |
return HuggingFaceModel(model_name, provider=hf_provider)
|
| 64 |
|
| 65 |
|
|
|
|
| 32 |
Explicitly passes API keys from settings to avoid requiring
|
| 33 |
users to export environment variables manually.
|
| 34 |
|
| 35 |
+
Priority: If OAuth token is available, prefer HuggingFace (even if provider is set to OpenAI).
|
| 36 |
+
This ensures users logged in via HuggingFace Spaces get the free tier.
|
| 37 |
+
|
| 38 |
Args:
|
| 39 |
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 40 |
"""
|
|
|
|
|
|
|
| 41 |
# Priority: oauth_token > env vars
|
| 42 |
effective_hf_token = oauth_token or settings.hf_token or settings.huggingface_api_key
|
| 43 |
|
| 44 |
+
# If OAuth token is available, prefer HuggingFace (free tier on Spaces)
|
| 45 |
+
if effective_hf_token:
|
| 46 |
+
model_name = settings.huggingface_model or "meta-llama/Llama-3.1-8B-Instruct"
|
| 47 |
+
hf_provider = HuggingFaceProvider(api_key=effective_hf_token)
|
| 48 |
+
logger.info(
|
| 49 |
+
"using_huggingface_with_token",
|
| 50 |
+
has_oauth=bool(oauth_token),
|
| 51 |
+
model=model_name,
|
| 52 |
+
)
|
| 53 |
+
return HuggingFaceModel(model_name, provider=hf_provider)
|
| 54 |
+
|
| 55 |
+
llm_provider = settings.llm_provider
|
| 56 |
+
|
| 57 |
if llm_provider == "anthropic":
|
| 58 |
+
if not settings.anthropic_api_key:
|
| 59 |
+
logger.warning("Anthropic provider selected but no API key available, defaulting to HuggingFace")
|
| 60 |
+
# Fallback to HuggingFace without token (public models)
|
| 61 |
+
model_name = settings.huggingface_model or "meta-llama/Llama-3.1-8B-Instruct"
|
| 62 |
+
hf_provider = HuggingFaceProvider(api_key=None)
|
| 63 |
+
return HuggingFaceModel(model_name, provider=hf_provider)
|
| 64 |
provider = AnthropicProvider(api_key=settings.anthropic_api_key)
|
| 65 |
return AnthropicModel(settings.anthropic_model, provider=provider)
|
| 66 |
|
| 67 |
if llm_provider == "huggingface":
|
| 68 |
+
# No token available, use public models
|
| 69 |
model_name = settings.huggingface_model or "meta-llama/Llama-3.1-8B-Instruct"
|
| 70 |
+
hf_provider = HuggingFaceProvider(api_key=None)
|
| 71 |
return HuggingFaceModel(model_name, provider=hf_provider)
|
| 72 |
|
| 73 |
if llm_provider == "openai":
|
| 74 |
+
if not settings.openai_api_key:
|
| 75 |
+
logger.warning("OpenAI provider selected but no API key available, defaulting to HuggingFace")
|
| 76 |
+
# Fallback to HuggingFace without token (public models)
|
| 77 |
+
model_name = settings.huggingface_model or "meta-llama/Llama-3.1-8B-Instruct"
|
| 78 |
+
hf_provider = HuggingFaceProvider(api_key=None)
|
| 79 |
+
return HuggingFaceModel(model_name, provider=hf_provider)
|
| 80 |
openai_provider = OpenAIProvider(api_key=settings.openai_api_key)
|
| 81 |
return OpenAIModel(settings.openai_model, provider=openai_provider)
|
| 82 |
|
| 83 |
# Default to HuggingFace if provider is unknown or not specified
|
| 84 |
+
if llm_provider not in ("huggingface", "openai", "anthropic"):
|
| 85 |
logger.warning("Unknown LLM provider, defaulting to HuggingFace", provider=llm_provider)
|
| 86 |
|
| 87 |
model_name = settings.huggingface_model or "meta-llama/Llama-3.1-8B-Instruct"
|
| 88 |
+
hf_provider = HuggingFaceProvider(api_key=None) # Public models
|
| 89 |
return HuggingFaceModel(model_name, provider=hf_provider)
|
| 90 |
|
| 91 |
|
src/agents/input_parser.py
CHANGED
|
@@ -152,12 +152,13 @@ class InputParserAgent:
|
|
| 152 |
)
|
| 153 |
|
| 154 |
|
| 155 |
-
def create_input_parser_agent(model: Any | None = None) -> InputParserAgent:
|
| 156 |
"""
|
| 157 |
Factory function to create an input parser agent.
|
| 158 |
|
| 159 |
Args:
|
| 160 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 161 |
|
| 162 |
Returns:
|
| 163 |
Configured InputParserAgent instance
|
|
@@ -168,7 +169,7 @@ def create_input_parser_agent(model: Any | None = None) -> InputParserAgent:
|
|
| 168 |
try:
|
| 169 |
# Get model from settings if not provided
|
| 170 |
if model is None:
|
| 171 |
-
model = get_model()
|
| 172 |
|
| 173 |
# Create and return input parser agent
|
| 174 |
return InputParserAgent(model=model)
|
|
|
|
| 152 |
)
|
| 153 |
|
| 154 |
|
| 155 |
+
def create_input_parser_agent(model: Any | None = None, oauth_token: str | None = None) -> InputParserAgent:
|
| 156 |
"""
|
| 157 |
Factory function to create an input parser agent.
|
| 158 |
|
| 159 |
Args:
|
| 160 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 161 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 162 |
|
| 163 |
Returns:
|
| 164 |
Configured InputParserAgent instance
|
|
|
|
| 169 |
try:
|
| 170 |
# Get model from settings if not provided
|
| 171 |
if model is None:
|
| 172 |
+
model = get_model(oauth_token=oauth_token)
|
| 173 |
|
| 174 |
# Create and return input parser agent
|
| 175 |
return InputParserAgent(model=model)
|
src/agents/knowledge_gap.py
CHANGED
|
@@ -132,12 +132,13 @@ HISTORY OF ACTIONS, FINDINGS AND THOUGHTS:
|
|
| 132 |
)
|
| 133 |
|
| 134 |
|
| 135 |
-
def create_knowledge_gap_agent(model: Any | None = None) -> KnowledgeGapAgent:
|
| 136 |
"""
|
| 137 |
Factory function to create a knowledge gap agent.
|
| 138 |
|
| 139 |
Args:
|
| 140 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 141 |
|
| 142 |
Returns:
|
| 143 |
Configured KnowledgeGapAgent instance
|
|
@@ -147,7 +148,7 @@ def create_knowledge_gap_agent(model: Any | None = None) -> KnowledgeGapAgent:
|
|
| 147 |
"""
|
| 148 |
try:
|
| 149 |
if model is None:
|
| 150 |
-
model = get_model()
|
| 151 |
|
| 152 |
return KnowledgeGapAgent(model=model)
|
| 153 |
|
|
|
|
| 132 |
)
|
| 133 |
|
| 134 |
|
| 135 |
+
def create_knowledge_gap_agent(model: Any | None = None, oauth_token: str | None = None) -> KnowledgeGapAgent:
|
| 136 |
"""
|
| 137 |
Factory function to create a knowledge gap agent.
|
| 138 |
|
| 139 |
Args:
|
| 140 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 141 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 142 |
|
| 143 |
Returns:
|
| 144 |
Configured KnowledgeGapAgent instance
|
|
|
|
| 148 |
"""
|
| 149 |
try:
|
| 150 |
if model is None:
|
| 151 |
+
model = get_model(oauth_token=oauth_token)
|
| 152 |
|
| 153 |
return KnowledgeGapAgent(model=model)
|
| 154 |
|
src/agents/long_writer.py
CHANGED
|
@@ -407,12 +407,13 @@ class LongWriterAgent:
|
|
| 407 |
return re.sub(r"^(#+)\s(.+)$", adjust_heading_level, section_markdown, flags=re.MULTILINE)
|
| 408 |
|
| 409 |
|
| 410 |
-
def create_long_writer_agent(model: Any | None = None) -> LongWriterAgent:
|
| 411 |
"""
|
| 412 |
Factory function to create a long writer agent.
|
| 413 |
|
| 414 |
Args:
|
| 415 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 416 |
|
| 417 |
Returns:
|
| 418 |
Configured LongWriterAgent instance
|
|
@@ -422,7 +423,7 @@ def create_long_writer_agent(model: Any | None = None) -> LongWriterAgent:
|
|
| 422 |
"""
|
| 423 |
try:
|
| 424 |
if model is None:
|
| 425 |
-
model = get_model()
|
| 426 |
|
| 427 |
return LongWriterAgent(model=model)
|
| 428 |
|
|
|
|
| 407 |
return re.sub(r"^(#+)\s(.+)$", adjust_heading_level, section_markdown, flags=re.MULTILINE)
|
| 408 |
|
| 409 |
|
| 410 |
+
def create_long_writer_agent(model: Any | None = None, oauth_token: str | None = None) -> LongWriterAgent:
|
| 411 |
"""
|
| 412 |
Factory function to create a long writer agent.
|
| 413 |
|
| 414 |
Args:
|
| 415 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 416 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 417 |
|
| 418 |
Returns:
|
| 419 |
Configured LongWriterAgent instance
|
|
|
|
| 423 |
"""
|
| 424 |
try:
|
| 425 |
if model is None:
|
| 426 |
+
model = get_model(oauth_token=oauth_token)
|
| 427 |
|
| 428 |
return LongWriterAgent(model=model)
|
| 429 |
|
src/agents/proofreader.py
CHANGED
|
@@ -181,12 +181,13 @@ REPORT DRAFT:
|
|
| 181 |
return f"# Research Report\n\n## Query\n{query}\n\n" + "\n\n".join(sections)
|
| 182 |
|
| 183 |
|
| 184 |
-
def create_proofreader_agent(model: Any | None = None) -> ProofreaderAgent:
|
| 185 |
"""
|
| 186 |
Factory function to create a proofreader agent.
|
| 187 |
|
| 188 |
Args:
|
| 189 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 190 |
|
| 191 |
Returns:
|
| 192 |
Configured ProofreaderAgent instance
|
|
@@ -196,7 +197,7 @@ def create_proofreader_agent(model: Any | None = None) -> ProofreaderAgent:
|
|
| 196 |
"""
|
| 197 |
try:
|
| 198 |
if model is None:
|
| 199 |
-
model = get_model()
|
| 200 |
|
| 201 |
return ProofreaderAgent(model=model)
|
| 202 |
|
|
|
|
| 181 |
return f"# Research Report\n\n## Query\n{query}\n\n" + "\n\n".join(sections)
|
| 182 |
|
| 183 |
|
| 184 |
+
def create_proofreader_agent(model: Any | None = None, oauth_token: str | None = None) -> ProofreaderAgent:
|
| 185 |
"""
|
| 186 |
Factory function to create a proofreader agent.
|
| 187 |
|
| 188 |
Args:
|
| 189 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 190 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 191 |
|
| 192 |
Returns:
|
| 193 |
Configured ProofreaderAgent instance
|
|
|
|
| 197 |
"""
|
| 198 |
try:
|
| 199 |
if model is None:
|
| 200 |
+
model = get_model(oauth_token=oauth_token)
|
| 201 |
|
| 202 |
return ProofreaderAgent(model=model)
|
| 203 |
|
src/agents/thinking.py
CHANGED
|
@@ -124,12 +124,13 @@ HISTORY OF ACTIONS, FINDINGS AND THOUGHTS:
|
|
| 124 |
return f"Starting iteration {iteration}. Need to gather information about: {query}"
|
| 125 |
|
| 126 |
|
| 127 |
-
def create_thinking_agent(model: Any | None = None) -> ThinkingAgent:
|
| 128 |
"""
|
| 129 |
Factory function to create a thinking agent.
|
| 130 |
|
| 131 |
Args:
|
| 132 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 133 |
|
| 134 |
Returns:
|
| 135 |
Configured ThinkingAgent instance
|
|
@@ -139,7 +140,7 @@ def create_thinking_agent(model: Any | None = None) -> ThinkingAgent:
|
|
| 139 |
"""
|
| 140 |
try:
|
| 141 |
if model is None:
|
| 142 |
-
model = get_model()
|
| 143 |
|
| 144 |
return ThinkingAgent(model=model)
|
| 145 |
|
|
|
|
| 124 |
return f"Starting iteration {iteration}. Need to gather information about: {query}"
|
| 125 |
|
| 126 |
|
| 127 |
+
def create_thinking_agent(model: Any | None = None, oauth_token: str | None = None) -> ThinkingAgent:
|
| 128 |
"""
|
| 129 |
Factory function to create a thinking agent.
|
| 130 |
|
| 131 |
Args:
|
| 132 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 133 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 134 |
|
| 135 |
Returns:
|
| 136 |
Configured ThinkingAgent instance
|
|
|
|
| 140 |
"""
|
| 141 |
try:
|
| 142 |
if model is None:
|
| 143 |
+
model = get_model(oauth_token=oauth_token)
|
| 144 |
|
| 145 |
return ThinkingAgent(model=model)
|
| 146 |
|
src/agents/tool_selector.py
CHANGED
|
@@ -144,12 +144,13 @@ HISTORY OF ACTIONS, FINDINGS AND THOUGHTS:
|
|
| 144 |
)
|
| 145 |
|
| 146 |
|
| 147 |
-
def create_tool_selector_agent(model: Any | None = None) -> ToolSelectorAgent:
|
| 148 |
"""
|
| 149 |
Factory function to create a tool selector agent.
|
| 150 |
|
| 151 |
Args:
|
| 152 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 153 |
|
| 154 |
Returns:
|
| 155 |
Configured ToolSelectorAgent instance
|
|
@@ -159,7 +160,7 @@ def create_tool_selector_agent(model: Any | None = None) -> ToolSelectorAgent:
|
|
| 159 |
"""
|
| 160 |
try:
|
| 161 |
if model is None:
|
| 162 |
-
model = get_model()
|
| 163 |
|
| 164 |
return ToolSelectorAgent(model=model)
|
| 165 |
|
|
|
|
| 144 |
)
|
| 145 |
|
| 146 |
|
| 147 |
+
def create_tool_selector_agent(model: Any | None = None, oauth_token: str | None = None) -> ToolSelectorAgent:
|
| 148 |
"""
|
| 149 |
Factory function to create a tool selector agent.
|
| 150 |
|
| 151 |
Args:
|
| 152 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 153 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 154 |
|
| 155 |
Returns:
|
| 156 |
Configured ToolSelectorAgent instance
|
|
|
|
| 160 |
"""
|
| 161 |
try:
|
| 162 |
if model is None:
|
| 163 |
+
model = get_model(oauth_token=oauth_token)
|
| 164 |
|
| 165 |
return ToolSelectorAgent(model=model)
|
| 166 |
|
src/agents/writer.py
CHANGED
|
@@ -185,12 +185,13 @@ FINDINGS:
|
|
| 185 |
)
|
| 186 |
|
| 187 |
|
| 188 |
-
def create_writer_agent(model: Any | None = None) -> WriterAgent:
|
| 189 |
"""
|
| 190 |
Factory function to create a writer agent.
|
| 191 |
|
| 192 |
Args:
|
| 193 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 194 |
|
| 195 |
Returns:
|
| 196 |
Configured WriterAgent instance
|
|
@@ -200,7 +201,7 @@ def create_writer_agent(model: Any | None = None) -> WriterAgent:
|
|
| 200 |
"""
|
| 201 |
try:
|
| 202 |
if model is None:
|
| 203 |
-
model = get_model()
|
| 204 |
|
| 205 |
return WriterAgent(model=model)
|
| 206 |
|
|
|
|
| 185 |
)
|
| 186 |
|
| 187 |
|
| 188 |
+
def create_writer_agent(model: Any | None = None, oauth_token: str | None = None) -> WriterAgent:
|
| 189 |
"""
|
| 190 |
Factory function to create a writer agent.
|
| 191 |
|
| 192 |
Args:
|
| 193 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 194 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 195 |
|
| 196 |
Returns:
|
| 197 |
Configured WriterAgent instance
|
|
|
|
| 201 |
"""
|
| 202 |
try:
|
| 203 |
if model is None:
|
| 204 |
+
model = get_model(oauth_token=oauth_token)
|
| 205 |
|
| 206 |
return WriterAgent(model=model)
|
| 207 |
|
src/middleware/state_machine.py
CHANGED
|
@@ -133,3 +133,4 @@ def get_workflow_state() -> WorkflowState:
|
|
| 133 |
|
| 134 |
|
| 135 |
|
|
|
|
|
|
| 133 |
|
| 134 |
|
| 135 |
|
| 136 |
+
|
src/orchestrator/graph_orchestrator.py
CHANGED
|
@@ -124,6 +124,7 @@ class GraphOrchestrator:
|
|
| 124 |
use_graph: bool = True,
|
| 125 |
search_handler: SearchHandlerProtocol | None = None,
|
| 126 |
judge_handler: JudgeHandlerProtocol | None = None,
|
|
|
|
| 127 |
) -> None:
|
| 128 |
"""
|
| 129 |
Initialize graph orchestrator.
|
|
@@ -135,6 +136,7 @@ class GraphOrchestrator:
|
|
| 135 |
use_graph: Whether to use graph execution (True) or agent chains (False)
|
| 136 |
search_handler: Optional search handler for tool execution
|
| 137 |
judge_handler: Optional judge handler for evidence assessment
|
|
|
|
| 138 |
"""
|
| 139 |
self.mode = mode
|
| 140 |
self.max_iterations = max_iterations
|
|
@@ -142,6 +144,7 @@ class GraphOrchestrator:
|
|
| 142 |
self.use_graph = use_graph
|
| 143 |
self.search_handler = search_handler
|
| 144 |
self.judge_handler = judge_handler
|
|
|
|
| 145 |
self.logger = logger
|
| 146 |
|
| 147 |
# Initialize flows (for backward compatibility)
|
|
@@ -256,6 +259,7 @@ class GraphOrchestrator:
|
|
| 256 |
max_iterations=self.max_iterations,
|
| 257 |
max_time_minutes=self.max_time_minutes,
|
| 258 |
judge_handler=self.judge_handler,
|
|
|
|
| 259 |
)
|
| 260 |
|
| 261 |
try:
|
|
@@ -291,6 +295,7 @@ class GraphOrchestrator:
|
|
| 291 |
self._deep_flow = DeepResearchFlow(
|
| 292 |
max_iterations=self.max_iterations,
|
| 293 |
max_time_minutes=self.max_time_minutes,
|
|
|
|
| 294 |
)
|
| 295 |
|
| 296 |
try:
|
|
@@ -322,11 +327,11 @@ class GraphOrchestrator:
|
|
| 322 |
Constructed ResearchGraph
|
| 323 |
"""
|
| 324 |
if mode == "iterative":
|
| 325 |
-
# Get agents
|
| 326 |
-
knowledge_gap_agent = create_knowledge_gap_agent()
|
| 327 |
-
tool_selector_agent = create_tool_selector_agent()
|
| 328 |
-
thinking_agent = create_thinking_agent()
|
| 329 |
-
writer_agent = create_writer_agent()
|
| 330 |
|
| 331 |
# Create graph
|
| 332 |
graph = create_iterative_graph(
|
|
@@ -336,13 +341,13 @@ class GraphOrchestrator:
|
|
| 336 |
writer_agent=writer_agent.agent,
|
| 337 |
)
|
| 338 |
else: # deep
|
| 339 |
-
# Get agents
|
| 340 |
-
planner_agent = create_planner_agent()
|
| 341 |
-
knowledge_gap_agent = create_knowledge_gap_agent()
|
| 342 |
-
tool_selector_agent = create_tool_selector_agent()
|
| 343 |
-
thinking_agent = create_thinking_agent()
|
| 344 |
-
writer_agent = create_writer_agent()
|
| 345 |
-
long_writer_agent = create_long_writer_agent()
|
| 346 |
|
| 347 |
# Create graph
|
| 348 |
graph = create_deep_graph(
|
|
@@ -610,7 +615,7 @@ class GraphOrchestrator:
|
|
| 610 |
)
|
| 611 |
|
| 612 |
# Get LongWriterAgent instance and call write_report directly
|
| 613 |
-
long_writer_agent = create_long_writer_agent()
|
| 614 |
final_report = await long_writer_agent.write_report(
|
| 615 |
original_query=query,
|
| 616 |
report_title=report_plan.report_title,
|
|
@@ -906,6 +911,7 @@ class GraphOrchestrator:
|
|
| 906 |
verbose=False, # Less verbose in parallel execution
|
| 907 |
use_graph=False, # Use agent chains for section research
|
| 908 |
judge_handler=self.judge_handler or judge_handler,
|
|
|
|
| 909 |
)
|
| 910 |
|
| 911 |
# Run research for this section
|
|
@@ -1008,7 +1014,7 @@ class GraphOrchestrator:
|
|
| 1008 |
"""
|
| 1009 |
try:
|
| 1010 |
# Use input parser agent for intelligent mode detection
|
| 1011 |
-
input_parser = create_input_parser_agent()
|
| 1012 |
parsed_query = await input_parser.parse(query)
|
| 1013 |
self.logger.info(
|
| 1014 |
"Research mode detected by input parser",
|
|
@@ -1048,6 +1054,7 @@ def create_graph_orchestrator(
|
|
| 1048 |
use_graph: bool = True,
|
| 1049 |
search_handler: SearchHandlerProtocol | None = None,
|
| 1050 |
judge_handler: JudgeHandlerProtocol | None = None,
|
|
|
|
| 1051 |
) -> GraphOrchestrator:
|
| 1052 |
"""
|
| 1053 |
Factory function to create a graph orchestrator.
|
|
@@ -1059,6 +1066,7 @@ def create_graph_orchestrator(
|
|
| 1059 |
use_graph: Whether to use graph execution (True) or agent chains (False)
|
| 1060 |
search_handler: Optional search handler for tool execution
|
| 1061 |
judge_handler: Optional judge handler for evidence assessment
|
|
|
|
| 1062 |
|
| 1063 |
Returns:
|
| 1064 |
Configured GraphOrchestrator instance
|
|
@@ -1070,4 +1078,5 @@ def create_graph_orchestrator(
|
|
| 1070 |
use_graph=use_graph,
|
| 1071 |
search_handler=search_handler,
|
| 1072 |
judge_handler=judge_handler,
|
|
|
|
| 1073 |
)
|
|
|
|
| 124 |
use_graph: bool = True,
|
| 125 |
search_handler: SearchHandlerProtocol | None = None,
|
| 126 |
judge_handler: JudgeHandlerProtocol | None = None,
|
| 127 |
+
oauth_token: str | None = None,
|
| 128 |
) -> None:
|
| 129 |
"""
|
| 130 |
Initialize graph orchestrator.
|
|
|
|
| 136 |
use_graph: Whether to use graph execution (True) or agent chains (False)
|
| 137 |
search_handler: Optional search handler for tool execution
|
| 138 |
judge_handler: Optional judge handler for evidence assessment
|
| 139 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 140 |
"""
|
| 141 |
self.mode = mode
|
| 142 |
self.max_iterations = max_iterations
|
|
|
|
| 144 |
self.use_graph = use_graph
|
| 145 |
self.search_handler = search_handler
|
| 146 |
self.judge_handler = judge_handler
|
| 147 |
+
self.oauth_token = oauth_token
|
| 148 |
self.logger = logger
|
| 149 |
|
| 150 |
# Initialize flows (for backward compatibility)
|
|
|
|
| 259 |
max_iterations=self.max_iterations,
|
| 260 |
max_time_minutes=self.max_time_minutes,
|
| 261 |
judge_handler=self.judge_handler,
|
| 262 |
+
oauth_token=self.oauth_token,
|
| 263 |
)
|
| 264 |
|
| 265 |
try:
|
|
|
|
| 295 |
self._deep_flow = DeepResearchFlow(
|
| 296 |
max_iterations=self.max_iterations,
|
| 297 |
max_time_minutes=self.max_time_minutes,
|
| 298 |
+
oauth_token=self.oauth_token,
|
| 299 |
)
|
| 300 |
|
| 301 |
try:
|
|
|
|
| 327 |
Constructed ResearchGraph
|
| 328 |
"""
|
| 329 |
if mode == "iterative":
|
| 330 |
+
# Get agents - pass OAuth token for HuggingFace authentication
|
| 331 |
+
knowledge_gap_agent = create_knowledge_gap_agent(oauth_token=self.oauth_token)
|
| 332 |
+
tool_selector_agent = create_tool_selector_agent(oauth_token=self.oauth_token)
|
| 333 |
+
thinking_agent = create_thinking_agent(oauth_token=self.oauth_token)
|
| 334 |
+
writer_agent = create_writer_agent(oauth_token=self.oauth_token)
|
| 335 |
|
| 336 |
# Create graph
|
| 337 |
graph = create_iterative_graph(
|
|
|
|
| 341 |
writer_agent=writer_agent.agent,
|
| 342 |
)
|
| 343 |
else: # deep
|
| 344 |
+
# Get agents - pass OAuth token for HuggingFace authentication
|
| 345 |
+
planner_agent = create_planner_agent(oauth_token=self.oauth_token)
|
| 346 |
+
knowledge_gap_agent = create_knowledge_gap_agent(oauth_token=self.oauth_token)
|
| 347 |
+
tool_selector_agent = create_tool_selector_agent(oauth_token=self.oauth_token)
|
| 348 |
+
thinking_agent = create_thinking_agent(oauth_token=self.oauth_token)
|
| 349 |
+
writer_agent = create_writer_agent(oauth_token=self.oauth_token)
|
| 350 |
+
long_writer_agent = create_long_writer_agent(oauth_token=self.oauth_token)
|
| 351 |
|
| 352 |
# Create graph
|
| 353 |
graph = create_deep_graph(
|
|
|
|
| 615 |
)
|
| 616 |
|
| 617 |
# Get LongWriterAgent instance and call write_report directly
|
| 618 |
+
long_writer_agent = create_long_writer_agent(oauth_token=self.oauth_token)
|
| 619 |
final_report = await long_writer_agent.write_report(
|
| 620 |
original_query=query,
|
| 621 |
report_title=report_plan.report_title,
|
|
|
|
| 911 |
verbose=False, # Less verbose in parallel execution
|
| 912 |
use_graph=False, # Use agent chains for section research
|
| 913 |
judge_handler=self.judge_handler or judge_handler,
|
| 914 |
+
oauth_token=self.oauth_token,
|
| 915 |
)
|
| 916 |
|
| 917 |
# Run research for this section
|
|
|
|
| 1014 |
"""
|
| 1015 |
try:
|
| 1016 |
# Use input parser agent for intelligent mode detection
|
| 1017 |
+
input_parser = create_input_parser_agent(oauth_token=self.oauth_token)
|
| 1018 |
parsed_query = await input_parser.parse(query)
|
| 1019 |
self.logger.info(
|
| 1020 |
"Research mode detected by input parser",
|
|
|
|
| 1054 |
use_graph: bool = True,
|
| 1055 |
search_handler: SearchHandlerProtocol | None = None,
|
| 1056 |
judge_handler: JudgeHandlerProtocol | None = None,
|
| 1057 |
+
oauth_token: str | None = None,
|
| 1058 |
) -> GraphOrchestrator:
|
| 1059 |
"""
|
| 1060 |
Factory function to create a graph orchestrator.
|
|
|
|
| 1066 |
use_graph: Whether to use graph execution (True) or agent chains (False)
|
| 1067 |
search_handler: Optional search handler for tool execution
|
| 1068 |
judge_handler: Optional judge handler for evidence assessment
|
| 1069 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 1070 |
|
| 1071 |
Returns:
|
| 1072 |
Configured GraphOrchestrator instance
|
|
|
|
| 1078 |
use_graph=use_graph,
|
| 1079 |
search_handler=search_handler,
|
| 1080 |
judge_handler=judge_handler,
|
| 1081 |
+
oauth_token=oauth_token,
|
| 1082 |
)
|
src/orchestrator/planner_agent.py
CHANGED
|
@@ -158,12 +158,13 @@ class PlannerAgent:
|
|
| 158 |
)
|
| 159 |
|
| 160 |
|
| 161 |
-
def create_planner_agent(model: Any | None = None) -> PlannerAgent:
|
| 162 |
"""
|
| 163 |
Factory function to create a planner agent.
|
| 164 |
|
| 165 |
Args:
|
| 166 |
model: Optional Pydantic AI model. If None, uses settings default.
|
|
|
|
| 167 |
|
| 168 |
Returns:
|
| 169 |
Configured PlannerAgent instance
|
|
@@ -174,7 +175,7 @@ def create_planner_agent(model: Any | None = None) -> PlannerAgent:
|
|
| 174 |
try:
|
| 175 |
# Get model from settings if not provided
|
| 176 |
if model is None:
|
| 177 |
-
model = get_model()
|
| 178 |
|
| 179 |
# Create and return planner agent
|
| 180 |
return PlannerAgent(model=model)
|
|
|
|
| 158 |
)
|
| 159 |
|
| 160 |
|
| 161 |
+
def create_planner_agent(model: Any | None = None, oauth_token: str | None = None) -> PlannerAgent:
|
| 162 |
"""
|
| 163 |
Factory function to create a planner agent.
|
| 164 |
|
| 165 |
Args:
|
| 166 |
model: Optional Pydantic AI model. If None, uses settings default.
|
| 167 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 168 |
|
| 169 |
Returns:
|
| 170 |
Configured PlannerAgent instance
|
|
|
|
| 175 |
try:
|
| 176 |
# Get model from settings if not provided
|
| 177 |
if model is None:
|
| 178 |
+
model = get_model(oauth_token=oauth_token)
|
| 179 |
|
| 180 |
# Create and return planner agent
|
| 181 |
return PlannerAgent(model=model)
|
src/orchestrator/research_flow.py
CHANGED
|
@@ -60,6 +60,7 @@ class IterativeResearchFlow:
|
|
| 60 |
verbose: bool = True,
|
| 61 |
use_graph: bool = False,
|
| 62 |
judge_handler: Any | None = None,
|
|
|
|
| 63 |
) -> None:
|
| 64 |
"""
|
| 65 |
Initialize iterative research flow.
|
|
@@ -69,19 +70,21 @@ class IterativeResearchFlow:
|
|
| 69 |
max_time_minutes: Maximum time in minutes
|
| 70 |
verbose: Whether to log progress
|
| 71 |
use_graph: Whether to use graph-based execution (True) or agent chains (False)
|
|
|
|
| 72 |
"""
|
| 73 |
self.max_iterations = max_iterations
|
| 74 |
self.max_time_minutes = max_time_minutes
|
| 75 |
self.verbose = verbose
|
| 76 |
self.use_graph = use_graph
|
|
|
|
| 77 |
self.logger = logger
|
| 78 |
|
| 79 |
# Initialize agents (only needed for agent chain execution)
|
| 80 |
if not use_graph:
|
| 81 |
-
self.knowledge_gap_agent = create_knowledge_gap_agent()
|
| 82 |
-
self.tool_selector_agent = create_tool_selector_agent()
|
| 83 |
-
self.thinking_agent = create_thinking_agent()
|
| 84 |
-
self.writer_agent = create_writer_agent()
|
| 85 |
# Initialize judge handler (use provided or create new)
|
| 86 |
self.judge_handler = judge_handler or create_judge_handler()
|
| 87 |
|
|
@@ -678,6 +681,7 @@ class DeepResearchFlow:
|
|
| 678 |
verbose: bool = True,
|
| 679 |
use_long_writer: bool = True,
|
| 680 |
use_graph: bool = False,
|
|
|
|
| 681 |
) -> None:
|
| 682 |
"""
|
| 683 |
Initialize deep research flow.
|
|
@@ -688,19 +692,21 @@ class DeepResearchFlow:
|
|
| 688 |
verbose: Whether to log progress
|
| 689 |
use_long_writer: Whether to use long writer (True) or proofreader (False)
|
| 690 |
use_graph: Whether to use graph-based execution (True) or agent chains (False)
|
|
|
|
| 691 |
"""
|
| 692 |
self.max_iterations = max_iterations
|
| 693 |
self.max_time_minutes = max_time_minutes
|
| 694 |
self.verbose = verbose
|
| 695 |
self.use_long_writer = use_long_writer
|
| 696 |
self.use_graph = use_graph
|
|
|
|
| 697 |
self.logger = logger
|
| 698 |
|
| 699 |
# Initialize agents (only needed for agent chain execution)
|
| 700 |
if not use_graph:
|
| 701 |
-
self.planner_agent = create_planner_agent()
|
| 702 |
-
self.long_writer_agent = create_long_writer_agent()
|
| 703 |
-
self.proofreader_agent = create_proofreader_agent()
|
| 704 |
# Initialize judge handler for section loop completion
|
| 705 |
self.judge_handler = create_judge_handler()
|
| 706 |
# Initialize budget tracker for token tracking
|
|
|
|
| 60 |
verbose: bool = True,
|
| 61 |
use_graph: bool = False,
|
| 62 |
judge_handler: Any | None = None,
|
| 63 |
+
oauth_token: str | None = None,
|
| 64 |
) -> None:
|
| 65 |
"""
|
| 66 |
Initialize iterative research flow.
|
|
|
|
| 70 |
max_time_minutes: Maximum time in minutes
|
| 71 |
verbose: Whether to log progress
|
| 72 |
use_graph: Whether to use graph-based execution (True) or agent chains (False)
|
| 73 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 74 |
"""
|
| 75 |
self.max_iterations = max_iterations
|
| 76 |
self.max_time_minutes = max_time_minutes
|
| 77 |
self.verbose = verbose
|
| 78 |
self.use_graph = use_graph
|
| 79 |
+
self.oauth_token = oauth_token
|
| 80 |
self.logger = logger
|
| 81 |
|
| 82 |
# Initialize agents (only needed for agent chain execution)
|
| 83 |
if not use_graph:
|
| 84 |
+
self.knowledge_gap_agent = create_knowledge_gap_agent(oauth_token=self.oauth_token)
|
| 85 |
+
self.tool_selector_agent = create_tool_selector_agent(oauth_token=self.oauth_token)
|
| 86 |
+
self.thinking_agent = create_thinking_agent(oauth_token=self.oauth_token)
|
| 87 |
+
self.writer_agent = create_writer_agent(oauth_token=self.oauth_token)
|
| 88 |
# Initialize judge handler (use provided or create new)
|
| 89 |
self.judge_handler = judge_handler or create_judge_handler()
|
| 90 |
|
|
|
|
| 681 |
verbose: bool = True,
|
| 682 |
use_long_writer: bool = True,
|
| 683 |
use_graph: bool = False,
|
| 684 |
+
oauth_token: str | None = None,
|
| 685 |
) -> None:
|
| 686 |
"""
|
| 687 |
Initialize deep research flow.
|
|
|
|
| 692 |
verbose: Whether to log progress
|
| 693 |
use_long_writer: Whether to use long writer (True) or proofreader (False)
|
| 694 |
use_graph: Whether to use graph-based execution (True) or agent chains (False)
|
| 695 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 696 |
"""
|
| 697 |
self.max_iterations = max_iterations
|
| 698 |
self.max_time_minutes = max_time_minutes
|
| 699 |
self.verbose = verbose
|
| 700 |
self.use_long_writer = use_long_writer
|
| 701 |
self.use_graph = use_graph
|
| 702 |
+
self.oauth_token = oauth_token
|
| 703 |
self.logger = logger
|
| 704 |
|
| 705 |
# Initialize agents (only needed for agent chain execution)
|
| 706 |
if not use_graph:
|
| 707 |
+
self.planner_agent = create_planner_agent(oauth_token=self.oauth_token)
|
| 708 |
+
self.long_writer_agent = create_long_writer_agent(oauth_token=self.oauth_token)
|
| 709 |
+
self.proofreader_agent = create_proofreader_agent(oauth_token=self.oauth_token)
|
| 710 |
# Initialize judge handler for section loop completion
|
| 711 |
self.judge_handler = create_judge_handler()
|
| 712 |
# Initialize budget tracker for token tracking
|
src/orchestrator_factory.py
CHANGED
|
@@ -46,6 +46,7 @@ def create_orchestrator(
|
|
| 46 |
judge_handler: JudgeHandlerProtocol | None = None,
|
| 47 |
config: OrchestratorConfig | None = None,
|
| 48 |
mode: Literal["simple", "magentic", "advanced", "iterative", "deep", "auto"] | None = None,
|
|
|
|
| 49 |
) -> Any:
|
| 50 |
"""
|
| 51 |
Create an orchestrator instance.
|
|
@@ -60,6 +61,7 @@ def create_orchestrator(
|
|
| 60 |
- "iterative": Knowledge-gap-driven research (Free Tier)
|
| 61 |
- "deep": Parallel section-based research (Free Tier)
|
| 62 |
- "auto": Intelligent mode detection (Free Tier)
|
|
|
|
| 63 |
|
| 64 |
Returns:
|
| 65 |
Orchestrator instance
|
|
@@ -83,6 +85,7 @@ def create_orchestrator(
|
|
| 83 |
use_graph=True,
|
| 84 |
search_handler=search_handler,
|
| 85 |
judge_handler=judge_handler,
|
|
|
|
| 86 |
)
|
| 87 |
|
| 88 |
# Simple mode requires handlers
|
|
|
|
| 46 |
judge_handler: JudgeHandlerProtocol | None = None,
|
| 47 |
config: OrchestratorConfig | None = None,
|
| 48 |
mode: Literal["simple", "magentic", "advanced", "iterative", "deep", "auto"] | None = None,
|
| 49 |
+
oauth_token: str | None = None,
|
| 50 |
) -> Any:
|
| 51 |
"""
|
| 52 |
Create an orchestrator instance.
|
|
|
|
| 61 |
- "iterative": Knowledge-gap-driven research (Free Tier)
|
| 62 |
- "deep": Parallel section-based research (Free Tier)
|
| 63 |
- "auto": Intelligent mode detection (Free Tier)
|
| 64 |
+
oauth_token: Optional OAuth token from HuggingFace login (takes priority over env vars)
|
| 65 |
|
| 66 |
Returns:
|
| 67 |
Orchestrator instance
|
|
|
|
| 85 |
use_graph=True,
|
| 86 |
search_handler=search_handler,
|
| 87 |
judge_handler=judge_handler,
|
| 88 |
+
oauth_token=oauth_token,
|
| 89 |
)
|
| 90 |
|
| 91 |
# Simple mode requires handlers
|
src/services/image_ocr.py
CHANGED
|
@@ -55,10 +55,11 @@ class ImageOCRService:
|
|
| 55 |
if self.client is None:
|
| 56 |
loop = asyncio.get_running_loop()
|
| 57 |
# Pass token to Client for authenticated Spaces
|
|
|
|
| 58 |
if token:
|
| 59 |
self.client = await loop.run_in_executor(
|
| 60 |
None,
|
| 61 |
-
lambda: Client(self.api_url,
|
| 62 |
)
|
| 63 |
else:
|
| 64 |
self.client = await loop.run_in_executor(
|
|
@@ -240,3 +241,4 @@ def get_image_ocr_service() -> ImageOCRService:
|
|
| 240 |
|
| 241 |
|
| 242 |
|
|
|
|
|
|
| 55 |
if self.client is None:
|
| 56 |
loop = asyncio.get_running_loop()
|
| 57 |
# Pass token to Client for authenticated Spaces
|
| 58 |
+
# Gradio Client uses 'token' parameter, not 'hf_token'
|
| 59 |
if token:
|
| 60 |
self.client = await loop.run_in_executor(
|
| 61 |
None,
|
| 62 |
+
lambda: Client(self.api_url, token=token),
|
| 63 |
)
|
| 64 |
else:
|
| 65 |
self.client = await loop.run_in_executor(
|
|
|
|
| 241 |
|
| 242 |
|
| 243 |
|
| 244 |
+
|
src/services/multimodal_processing.py
CHANGED
|
@@ -134,3 +134,4 @@ def get_multimodal_service() -> MultimodalService:
|
|
| 134 |
|
| 135 |
|
| 136 |
|
|
|
|
|
|
| 134 |
|
| 135 |
|
| 136 |
|
| 137 |
+
|
src/services/stt_gradio.py
CHANGED
|
@@ -54,10 +54,11 @@ class STTService:
|
|
| 54 |
if self.client is None:
|
| 55 |
loop = asyncio.get_running_loop()
|
| 56 |
# Pass token to Client for authenticated Spaces
|
|
|
|
| 57 |
if token:
|
| 58 |
self.client = await loop.run_in_executor(
|
| 59 |
None,
|
| 60 |
-
lambda: Client(self.api_url,
|
| 61 |
)
|
| 62 |
else:
|
| 63 |
self.client = await loop.run_in_executor(
|
|
|
|
| 54 |
if self.client is None:
|
| 55 |
loop = asyncio.get_running_loop()
|
| 56 |
# Pass token to Client for authenticated Spaces
|
| 57 |
+
# Gradio Client uses 'token' parameter, not 'hf_token'
|
| 58 |
if token:
|
| 59 |
self.client = await loop.run_in_executor(
|
| 60 |
None,
|
| 61 |
+
lambda: Client(self.api_url, token=token),
|
| 62 |
)
|
| 63 |
else:
|
| 64 |
self.client = await loop.run_in_executor(
|
src/tools/crawl_adapter.py
CHANGED
|
@@ -62,3 +62,4 @@ async def crawl_website(starting_url: str) -> str:
|
|
| 62 |
|
| 63 |
|
| 64 |
|
|
|
|
|
|
| 62 |
|
| 63 |
|
| 64 |
|
| 65 |
+
|
src/tools/web_search_adapter.py
CHANGED
|
@@ -67,3 +67,4 @@ async def web_search(query: str) -> str:
|
|
| 67 |
|
| 68 |
|
| 69 |
|
|
|
|
|
|
| 67 |
|
| 68 |
|
| 69 |
|
| 70 |
+
|
src/utils/config.py
CHANGED
|
@@ -24,7 +24,7 @@ class Settings(BaseSettings):
|
|
| 24 |
openai_api_key: str | None = Field(default=None, description="OpenAI API key")
|
| 25 |
anthropic_api_key: str | None = Field(default=None, description="Anthropic API key")
|
| 26 |
llm_provider: Literal["openai", "anthropic", "huggingface"] = Field(
|
| 27 |
-
default="
|
| 28 |
)
|
| 29 |
openai_model: str = Field(default="gpt-5.1", description="OpenAI model name")
|
| 30 |
anthropic_model: str = Field(
|
|
@@ -140,62 +140,6 @@ class Settings(BaseSettings):
|
|
| 140 |
description="Automatically ingest evidence into RAG",
|
| 141 |
)
|
| 142 |
|
| 143 |
-
# Audio Processing Configuration
|
| 144 |
-
tts_model: str = Field(
|
| 145 |
-
default="hexgrad/Kokoro-82M",
|
| 146 |
-
description="Kokoro TTS model ID for text-to-speech",
|
| 147 |
-
)
|
| 148 |
-
tts_voice: str = Field(
|
| 149 |
-
default="af_heart",
|
| 150 |
-
description="Kokoro voice ID (e.g., af_heart, af_bella, am_michael)",
|
| 151 |
-
)
|
| 152 |
-
tts_speed: float = Field(
|
| 153 |
-
default=1.0,
|
| 154 |
-
ge=0.5,
|
| 155 |
-
le=2.0,
|
| 156 |
-
description="TTS speech speed multiplier",
|
| 157 |
-
)
|
| 158 |
-
tts_gpu: str | None = Field(
|
| 159 |
-
default="T4",
|
| 160 |
-
description="Modal GPU type for TTS (T4, A10, A100, etc.)",
|
| 161 |
-
)
|
| 162 |
-
tts_timeout: int = Field(
|
| 163 |
-
default=60,
|
| 164 |
-
ge=10,
|
| 165 |
-
le=300,
|
| 166 |
-
description="TTS synthesis timeout in seconds",
|
| 167 |
-
)
|
| 168 |
-
stt_api_url: str = Field(
|
| 169 |
-
default="nvidia/canary-1b-v2",
|
| 170 |
-
description="Gradio Space URL for STT API (nvidia/canary-1b-v2)",
|
| 171 |
-
)
|
| 172 |
-
stt_source_lang: str = Field(
|
| 173 |
-
default="English",
|
| 174 |
-
description="Source language for STT transcription",
|
| 175 |
-
)
|
| 176 |
-
stt_target_lang: str = Field(
|
| 177 |
-
default="English",
|
| 178 |
-
description="Target language for STT transcription",
|
| 179 |
-
)
|
| 180 |
-
enable_audio_input: bool = Field(
|
| 181 |
-
default=True,
|
| 182 |
-
description="Enable audio input (microphone/file upload)",
|
| 183 |
-
)
|
| 184 |
-
enable_audio_output: bool = Field(
|
| 185 |
-
default=True,
|
| 186 |
-
description="Enable audio output (TTS response)",
|
| 187 |
-
)
|
| 188 |
-
|
| 189 |
-
# Image OCR Configuration
|
| 190 |
-
ocr_api_url: str = Field(
|
| 191 |
-
default="prithivMLmods/Multimodal-OCR3",
|
| 192 |
-
description="Gradio Space URL for image OCR API",
|
| 193 |
-
)
|
| 194 |
-
enable_image_input: bool = Field(
|
| 195 |
-
default=True,
|
| 196 |
-
description="Enable image input (file upload with OCR)",
|
| 197 |
-
)
|
| 198 |
-
|
| 199 |
@property
|
| 200 |
def modal_available(self) -> bool:
|
| 201 |
"""Check if Modal credentials are configured."""
|
|
@@ -259,16 +203,6 @@ class Settings(BaseSettings):
|
|
| 259 |
return bool(self.tavily_api_key)
|
| 260 |
return False
|
| 261 |
|
| 262 |
-
@property
|
| 263 |
-
def audio_available(self) -> bool:
|
| 264 |
-
"""Check if audio processing is available (Modal + STT API)."""
|
| 265 |
-
return self.modal_available and bool(self.stt_api_url)
|
| 266 |
-
|
| 267 |
-
@property
|
| 268 |
-
def image_ocr_available(self) -> bool:
|
| 269 |
-
"""Check if image OCR is available (OCR API URL configured)."""
|
| 270 |
-
return bool(self.ocr_api_url)
|
| 271 |
-
|
| 272 |
|
| 273 |
def get_settings() -> Settings:
|
| 274 |
"""Factory function to get settings (allows mocking in tests)."""
|
|
|
|
| 24 |
openai_api_key: str | None = Field(default=None, description="OpenAI API key")
|
| 25 |
anthropic_api_key: str | None = Field(default=None, description="Anthropic API key")
|
| 26 |
llm_provider: Literal["openai", "anthropic", "huggingface"] = Field(
|
| 27 |
+
default="huggingface", description="Which LLM provider to use"
|
| 28 |
)
|
| 29 |
openai_model: str = Field(default="gpt-5.1", description="OpenAI model name")
|
| 30 |
anthropic_model: str = Field(
|
|
|
|
| 140 |
description="Automatically ingest evidence into RAG",
|
| 141 |
)
|
| 142 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
@property
|
| 144 |
def modal_available(self) -> bool:
|
| 145 |
"""Check if Modal credentials are configured."""
|
|
|
|
| 203 |
return bool(self.tavily_api_key)
|
| 204 |
return False
|
| 205 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
def get_settings() -> Settings:
|
| 208 |
"""Factory function to get settings (allows mocking in tests)."""
|
tests/unit/middleware/__init__.py
CHANGED
|
@@ -18,4 +18,5 @@
|
|
| 18 |
|
| 19 |
|
| 20 |
|
|
|
|
| 21 |
|
|
|
|
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
+
|
| 22 |
|
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 |
|