vn6295337's picture
Rename financials to fundamentals across frontend and backend
5766b78
"""
Analysis and workflow route handlers.
Handles SWOT analysis workflow lifecycle.
"""
import uuid
import threading
from fastapi import APIRouter, HTTPException
from src.api.schemas import AnalysisRequest, WorkflowStartResponse
from src.services.workflow_store import (
WORKFLOWS,
add_activity_log,
add_metric,
run_workflow_background,
)
router = APIRouter()
@router.post("/analyze", response_model=WorkflowStartResponse)
async def start_analysis(request: AnalysisRequest):
"""Start a new SWOT analysis workflow."""
workflow_id = str(uuid.uuid4())
# Initialize workflow state
WORKFLOWS[workflow_id] = {
"status": "starting",
"current_step": "input",
"revision_count": 0,
"score": 0,
"company_name": request.name,
"ticker": request.ticker,
"strategy_focus": request.strategy_focus,
"activity_log": [],
"metrics": [],
"mcp_status": {
"fundamentals": "idle",
"valuation": "idle",
"volatility": "idle",
"macro": "idle",
"news": "idle",
"sentiment": "idle"
},
"llm_status": {
"groq": "idle",
"gemini": "idle",
"openrouter": "idle"
}
}
# Start workflow in background thread
thread = threading.Thread(
target=run_workflow_background,
args=(workflow_id, request.name, request.ticker, request.strategy_focus,
request.skip_cache, request.user_api_keys),
daemon=True
)
thread.start()
return {"workflow_id": workflow_id}
@router.get("/workflow/{workflow_id}/status")
async def get_workflow_status(workflow_id: str):
"""Get current status of a workflow."""
if workflow_id not in WORKFLOWS:
raise HTTPException(status_code=404, detail="Workflow not found")
workflow = WORKFLOWS[workflow_id]
response = {
"status": workflow.get("status", "unknown"),
"current_step": workflow.get("current_step", "unknown"),
"revision_count": workflow.get("revision_count", 0),
"score": workflow.get("score", 0),
"activity_log": workflow.get("activity_log", []),
"metrics": workflow.get("metrics", []),
"mcp_status": workflow.get("mcp_status", {}),
"llm_status": workflow.get("llm_status", {}),
"provider_used": workflow.get("provider_used"),
"data_source": workflow.get("data_source")
}
# Include error message for error/aborted states
if workflow.get("status") in ("error", "aborted"):
response["error"] = workflow.get("error", "Unknown error")
return response
@router.post("/workflow/{workflow_id}/retry-mcp/{mcp_name}")
async def retry_mcp_server(workflow_id: str, mcp_name: str):
"""
Retry fetching data from a specific MCP server.
Note: MCP servers are now managed by the external Research Service.
Individual MCP retries are not available in this architecture.
Start a new analysis to refresh all data.
"""
raise HTTPException(
status_code=501,
detail="MCP retry not available. MCP servers are managed by the external Research Service. Please start a new analysis to refresh data."
)
@router.get("/workflow/{workflow_id}/result")
async def get_workflow_result(workflow_id: str):
"""Get final result of a completed workflow."""
if workflow_id not in WORKFLOWS:
raise HTTPException(status_code=404, detail="Workflow not found")
workflow = WORKFLOWS[workflow_id]
if workflow.get("status") != "completed":
raise HTTPException(
status_code=400,
detail=f"Workflow not completed. Status: {workflow.get('status')}"
)
return workflow.get("result", {})