vn6295337's picture
Initial commit: Instant SWOT Agent
0c591a7
raw
history blame
5.35 kB
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)
@traceable(name="Editor")
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