Spaces:
Sleeping
Sleeping
| from src.llm_client import get_llm_client | |
| from langsmith import traceable | |
| import time | |
| import json | |
| def _add_activity_log(workflow_id, progress_store, step, message): | |
| """Helper to add activity log entry.""" | |
| if workflow_id and progress_store: | |
| from src.services.workflow_store import add_activity_log | |
| add_activity_log(workflow_id, step, message) | |
| def editor_node(state, workflow_id=None, progress_store=None): | |
| """ | |
| Editor node that revises the SWOT draft based on critique feedback. | |
| Increments the revision count and returns the improved draft. | |
| """ | |
| # Extract workflow_id and progress_store from state (graph invokes with state only) | |
| if workflow_id is None: | |
| workflow_id = state.get("workflow_id") | |
| if progress_store is None: | |
| progress_store = state.get("progress_store") | |
| current_revision = state.get("revision_count", 0) + 1 | |
| # Update progress if tracking is enabled | |
| if workflow_id and progress_store: | |
| progress_store[workflow_id].update({ | |
| "current_step": "editor", | |
| "revision_count": state.get("revision_count", 0), | |
| "score": state.get("score", 0) | |
| }) | |
| # Skip if workflow already has an error (abort mode) | |
| if state.get("error"): | |
| _add_activity_log(workflow_id, progress_store, "editor", f"Skipping revision - workflow aborted") | |
| state["revision_count"] = current_revision | |
| return state | |
| # Log revision start | |
| _add_activity_log(workflow_id, progress_store, "editor", f"Revision #{current_revision} in progress...") | |
| llm = get_llm_client() | |
| strategy_name = state.get("strategy_focus", "Cost Leadership") | |
| # Get source data for grounding - editor must use ONLY this data | |
| source_data = state.get("raw_data", "") | |
| # Truncate if too long to avoid token limits | |
| if len(source_data) > 4000: | |
| source_data = source_data[:4000] + "\n... [truncated]" | |
| # Prepare the revision prompt with source data for grounding | |
| prompt = f""" | |
| You are revising a SWOT analysis based on critique feedback. | |
| CRITICAL GROUNDING RULES: | |
| 1. You may ONLY use facts and numbers from the SOURCE DATA provided below. | |
| 2. DO NOT invent, assume, or fabricate any information not in the source data. | |
| 3. Every claim must cite specific numbers from the source data. | |
| 4. If the critique asks for information not in the source data, state "Data not available". | |
| SOURCE DATA (use ONLY this for facts and numbers): | |
| {source_data} | |
| CURRENT DRAFT: | |
| {state['draft_report']} | |
| CRITIQUE: | |
| {state['critique']} | |
| Strategic Focus: {strategy_name} | |
| REVISION INSTRUCTIONS: | |
| 1. Address the critique points using ONLY data from SOURCE DATA above | |
| 2. Ensure all 4 SWOT sections are present and complete | |
| 3. Every bullet point must cite specific metrics from the source data | |
| 4. Make sure strengths/opportunities are positive, weaknesses/threats are negative | |
| 5. Align analysis with {strategy_name} strategic focus | |
| 6. If data is missing for a point, remove that point rather than inventing data | |
| Return only the improved SWOT analysis. Do NOT include any facts not found in the SOURCE DATA. | |
| """ | |
| # Get the revised draft from LLM | |
| start_time = time.time() | |
| response, provider, error, providers_failed = llm.query(prompt, temperature=0) | |
| elapsed = time.time() - start_time | |
| # Log failed providers | |
| for pf in providers_failed: | |
| _add_activity_log(workflow_id, progress_store, "editor", f"LLM {pf['name']} failed: {pf['error']}") | |
| # Track failed providers in state for frontend | |
| if "llm_providers_failed" not in state: | |
| state["llm_providers_failed"] = [] | |
| state["llm_providers_failed"].extend([pf["name"] for pf in providers_failed]) | |
| if error: | |
| print(f"Editor LLM error: {error}") | |
| _add_activity_log(workflow_id, progress_store, "editor", f"Revision failed: {error}") | |
| # Graceful degradation based on revision count | |
| if current_revision == 1: | |
| # First revision failed - use Analyzer's original draft | |
| _add_activity_log(workflow_id, progress_store, "editor", "Using initial draft from Analyzer (revision unavailable)") | |
| # Don't set error - allow workflow to continue with original draft | |
| # draft_report already contains Analyzer's output | |
| state["editor_skipped"] = True | |
| else: | |
| # Revision > 1 failed - use the last successful revision | |
| _add_activity_log(workflow_id, progress_store, "editor", f"Using revision #{current_revision - 1} draft (further revision unavailable)") | |
| # Don't set error - allow workflow to complete with previous draft | |
| state["editor_skipped"] = True | |
| else: | |
| state["draft_report"] = response | |
| state["provider_used"] = provider | |
| state["editor_skipped"] = False | |
| _add_activity_log(workflow_id, progress_store, "editor", f"Revision #{current_revision} completed via {provider} ({elapsed:.1f}s)") | |
| # Increment revision count | |
| state["revision_count"] = current_revision | |
| # Update progress with new revision count | |
| if workflow_id and progress_store: | |
| progress_store[workflow_id].update({ | |
| "current_step": "editor", | |
| "revision_count": state["revision_count"], | |
| "score": state.get("score", 0) | |
| }) | |
| return state | |