Files
ALwrity/docs/Research/RESEARCH_EXECUTION_FLOW.md

16 KiB

Research Execution Flow - Code Walkthrough

Overview

This document traces the complete flow from when a user clicks "Start Research" to when they see the results on the UI.


1. User Clicks "Start Research" Button

Location: ActionButtons.tsx (Line 104-119)

<Button
  variant="contained"
  color="primary"
  startIcon={isExecuting ? <CircularProgress size={16} color="inherit" /> : <PlayIcon />}
  onClick={onExecute}  // ← This is the entry point
  disabled={isExecuting || !canExecute}
>
  {isExecuting ? 'Researching...' : 'Start Research'}
</Button>

What happens:

  • Button shows loading spinner when isExecuting is true
  • Calls onExecute callback
  • Button is disabled if no queries are selected (canExecute must be true)

2. IntentConfirmationPanel Handles Execution

Location: IntentConfirmationPanel.tsx (Line 106-122)

const handleExecute = () => {
  const updatedIntent = { ...intent };
  // Pass wizard state to onConfirm for draft saving
  onConfirm(updatedIntent, wizardState);
  
  // Get selected queries (sorted by priority)
  const queriesToUse = Array.from(selectedQueries)
    .sort((a, b) => a - b)
    .map(idx => editedQueries[idx])
    .filter(q => q && q.query.trim().length > 0);
  
  // Store updated trends config
  if (editedTrendsConfig && intentAnalysis) {
    intentAnalysis.trends_config = editedTrendsConfig;
  }
  
  onExecute(queriesToUse);  // ← Passes queries to ResearchInput
};

What happens:

  1. Confirms the intent (saves draft)
  2. Extracts selected queries from the UI
  3. Updates trends configuration if modified
  4. Calls onExecute with the selected queries

3. ResearchInput Passes to Execution Hook

Location: ResearchInput.tsx (Line 580-586)

onExecute={async (selectedQueries) => {
  const result = await execution.executeIntentResearch(state, selectedQueries);
  if (result?.success) {
    // Skip to results step
    onUpdate({ currentStep: 3 });
  }
}}

What happens:

  1. Calls execution.executeIntentResearch() with wizard state and selected queries
  2. If successful, automatically navigates to Step 3 (Results)

4. Execution Hook Processes Research

Location: useResearchExecution.ts (Line 284-378)

const executeIntentResearch = useCallback(async (
  state: WizardState,
  selectedQueries?: ResearchQuery[]
): Promise<IntentDrivenResearchResponse | null> => {
  // 1. Ensure intent is available
  let intent = confirmedIntent;
  if (!intent) {
    const analysis = await analyzeIntent(state);
    if (!analysis?.success) {
      return null;
    }
    intent = analysis.intent;
  }

  // 2. Set loading state
  setIsExecuting(true);
  setError(null);

  try {
    // 3. Prepare queries (use provided or fall back to suggested)
    const queriesToUse = selectedQueries || 
      intentAnalysis?.suggested_queries?.slice(0, 5) || [];
    
    // 4. Make API call
    const response = await intentResearchApi.executeIntentResearch({
      user_input: state.keywords.join(' '),
      confirmed_intent: intent,
      selected_queries: queriesToUse.map(q => ({
        query: q.query,
        purpose: q.purpose,
        provider: q.provider,
        priority: q.priority,
        expected_results: q.expected_results,
      })),
      max_sources: state.config.max_sources || 10,
      include_domains: state.config.exa_include_domains || 
        state.config.tavily_include_domains || [],
      exclude_domains: state.config.exa_exclude_domains || 
        state.config.tavily_exclude_domains || [],
      trends_config: intentAnalysis?.trends_config,
      skip_inference: true,
    });

    // 5. Handle response
    if (!response.success) {
      setError(response.error_message || 'Research failed');
      setIsExecuting(false);
      return null;
    }

    // 6. Store results
    setIntentResult(response);
    
    // 7. Save draft to database
    autoSaveDraft(state, {
      intentAnalysis: intentAnalysis || undefined,
      confirmedIntent: intent,
      intentResult: response,
    }).catch(error => {
      console.warn('[useResearchExecution] Failed to save draft:', error);
    });
    
    // 8. Transform to legacy format for backward compatibility
    const legacyResult = {
      success: true,
      sources: response.sources.map(s => ({
        title: s.title,
        url: s.url,
        excerpt: s.excerpt ?? undefined,
        credibility_score: s.credibility_score,
      })),
      keyword_analysis: {
        primary_keywords: state.keywords,
        secondary: response.suggested_outline,
      },
      competitor_analysis: {},
      suggested_angles: response.key_takeaways,
      search_queries: [],
      intent_result: response,
    };
    
    setResult(legacyResult);
    setIsExecuting(false);
    
    // 9. Cache result
    researchCache.cacheResult(
      state.keywords,
      state.industry,
      state.targetAudience,
      legacyResult
    );
    
    return response;
  } catch (err) {
    const errorMessage = err instanceof Error ? err.message : 'Research failed';
    setError(errorMessage);
    setIsExecuting(false);
    return null;
  }
}, [confirmedIntent, intentAnalysis, analyzeIntent]);

What happens:

  1. Validates intent is available
  2. Sets isExecuting = true (shows loading state)
  3. Prepares queries from selection or defaults
  4. Makes API call to /api/research/intent/research
  5. Handles success/error responses
  6. Saves draft to database
  7. Transforms result to legacy format
  8. Caches result for future use
  9. Sets isExecuting = false (hides loading state)

5. API Call to Backend

Location: intentResearchApi.ts (Line 50-114)

export const executeIntentResearch = async (
  request: IntentDrivenResearchRequest
): Promise<IntentDrivenResearchResponse> => {
  try {
    const response = await axios.post(
      '/api/research/intent/research',
      request,
      {
        headers: {
          'Content-Type': 'application/json',
        },
        timeout: 300000, // 5 minutes
      }
    );
    return response.data;
  } catch (error: any) {
    // Error handling...
  }
};

Backend Endpoint: POST /api/research/intent/research

Backend Handler: backend/api/research/handlers/intent.py (Line 619-809)

What backend does:

  1. Validates authentication
  2. Gets research persona
  3. Determines intent (from confirmed or infers)
  4. Generates queries if not provided
  5. Executes research using Research Engine
  6. Runs Google Trends analysis in parallel (if enabled)
  7. Analyzes results using IntentAwareAnalyzer
  8. Merges trends data
  9. Returns structured response

6. UI Updates During Execution

Loading State Changes:

  1. Button State (ActionButtons.tsx):

    • Text changes: "Start Research" → "Researching..."
    • Shows spinner icon
    • Button is disabled
  2. Execution Hook State (useResearchExecution.ts):

    • isExecuting = true → triggers re-renders
    • error = null → clears any previous errors
  3. ResearchInput Component (ResearchInput.tsx):

    • execution.isExecuting prop updates
    • IntentConfirmationPanel shows loading state

7. Navigation to Results Step

Location: ResearchInput.tsx (Line 580-586)

onExecute={async (selectedQueries) => {
  const result = await execution.executeIntentResearch(state, selectedQueries);
  if (result?.success) {
    // Skip to results step
    onUpdate({ currentStep: 3 });  // ← Navigates to Step 3
  }
}}

What happens:

  • After successful research, automatically updates wizard state
  • currentStep changes from 1 to 3
  • ResearchWizard re-renders and shows StepResults component

8. Results Display - StepResults Component

Location: StepResults.tsx (Line 15-405)

Initial Check (Line 19-35):

// Check if we have intent-driven results
const intentResult: IntentDrivenResearchResponse | null = 
  execution?.intentResult || 
  (state.results as any)?.intent_result || 
  null;
  
// Determine if we have both types of results
const hasIntentResults = !!intentResult;
const hasTraditionalResults = !!state.results && !intentResult;
const hasAnyResults = hasIntentResults || hasTraditionalResults;

if (!hasAnyResults) {
  return (
    <div style={{ padding: '24px', textAlign: 'center' }}>
      <p style={{ color: '#666' }}>No results available</p>
    </div>
  );
}

Header Section (Line 73-134):

What user sees:

  • Title: "Research Results"
  • Action Buttons:
    • ← Back (returns to previous step)
    • 📥 Export JSON (downloads results)
    • 🔄 Start New Research (resets to step 1)

Tab Navigation (Line 144-210):

Tabs available:

  1. 📋 Summary - Executive summary and key takeaways
  2. 📊 Deliverables - Statistics, quotes, case studies, trends
  3. 🔗 Sources - All research sources with links
  4. 📈 Analysis - Detailed analysis and insights

Tab badges show counts:

  • Deliverables tab: Total count of all deliverables
  • Sources tab: Number of sources found

Summary Tab Content (Line 217-232):

What user sees:

  1. Executive Summary (if available):

    {intentResult.executive_summary && (
      <div style={{
        backgroundColor: '#f0f9ff',
        border: '1px solid #bae6fd',
        borderRadius: '8px',
        padding: '16px',
      }}>
        <h4>Executive Summary</h4>
        <p>{intentResult.executive_summary}</p>
      </div>
    )}
    
  2. Direct Answer (if available):

    {intentResult.primary_answer && (
      <div style={{
        backgroundColor: '#f0fdf4',
        border: '1px solid #86efac',
      }}>
        <h4>Direct Answer</h4>
        <p>{intentResult.primary_answer}</p>
      </div>
    )}
    
  3. Key Takeaways (if available):

    • List of bullet points
    • Styled as cards or list items

Deliverables Tab Content (Line 250-280):

What user sees:

  1. Statistics (intentResult.statistics):

    • Data points with labels
    • Formatted as cards or tables
    • May include charts/graphs
  2. Expert Quotes (intentResult.expert_quotes):

    • Quote text
    • Source attribution
    • Credibility score
  3. Case Studies (intentResult.case_studies):

    • Case study title
    • Description
    • Key findings
    • Source link
  4. Trends (intentResult.trends):

    • Trend description
    • Google Trends data (if available)
    • Charts showing interest over time
    • Regional interest data
  5. Best Practices (intentResult.best_practices):

    • List of actionable recommendations
  6. Comparisons (intentResult.comparisons):

    • Side-by-side comparisons
    • Pros/cons tables

Sources Tab Content (Line 290-320):

What user sees:

{intentResult.sources.map((source, idx) => (
  <div key={idx} style={{
    border: '1px solid #e0e0e0',
    borderRadius: '8px',
    padding: '12px',
    marginBottom: '12px',
  }}>
    <h4>
      <a href={source.url} target="_blank" rel="noopener noreferrer">
        {source.title}
      </a>
    </h4>
    {source.excerpt && <p>{source.excerpt}</p>}
    <div>
      <span>Credibility: {source.credibility_score}</span>
      <span>Domain: {new URL(source.url).hostname}</span>
    </div>
  </div>
))}

Each source shows:

  • Title (clickable link)
  • Excerpt/preview
  • Credibility score
  • Domain name
  • Published date (if available)

Analysis Tab Content (Line 330-360):

What user sees:

  1. Confidence Score:

    • Visual indicator (progress bar or badge)
    • Percentage or rating
  2. Gaps Identified:

    • List of areas needing more research
    • Suggestions for follow-up
  3. Follow-up Queries:

    • Suggested next research questions
    • Clickable to start new research
  4. Suggested Outline:

    • Content structure based on research
    • Organized by sections

9. IntentResultsDisplay Component

Location: IntentResultsDisplay.tsx

Used when: Intent-driven results are available

Features:

  • Tabbed interface for different deliverable types
  • Interactive charts for trends
  • Expandable sections for detailed views
  • Export functionality for trends data

Tabs:

  1. Summary - Overview and primary answer
  2. Statistics - Data points and metrics
  3. Expert Quotes - Quotations with sources
  4. Case Studies - Real-world examples
  5. Trends - Trend analysis with charts
  6. Sources - All research sources

10. State Management After Completion

Draft Saving (Line 330-337):

// Save draft with research results
autoSaveDraft(state, {
  intentAnalysis: intentAnalysis || undefined,
  confirmedIntent: intent,
  intentResult: response,
}).catch(error => {
  console.warn('[useResearchExecution] Failed to save draft:', error);
});

What happens:

  • Saves complete research state to:
    1. localStorage (for browser persistence)
    2. Database (via /api/research/projects/save)

Saved data includes:

  • Keywords
  • Intent analysis
  • Confirmed intent
  • Research results
  • Configuration
  • Current step (3 = completed)

Result Caching (Line 363-369):

researchCache.cacheResult(
  state.keywords,
  state.industry,
  state.targetAudience,
  legacyResult
);

Purpose: Allows quick retrieval of results for similar queries


11. User Actions After Results

Available Actions:

  1. ← Back Button:

    • Returns to Step 1 (Research Input)
    • Preserves all data
  2. 📥 Export JSON:

    • Downloads complete results as JSON file
    • Includes all deliverables, sources, and metadata
  3. 🔄 Start New Research:

    • Resets wizard to Step 1
    • Clears all results
    • Starts fresh research
  4. Tab Navigation:

    • Switch between Summary, Deliverables, Sources, Analysis
    • Each tab shows different aspect of results

12. Error Handling

If Research Fails:

  1. API Error:

    • setError(errorMessage) in execution hook
    • Error displayed in UI
    • Button re-enabled for retry
  2. Network Error:

    • Timeout after 5 minutes
    • User sees "Network error" message
    • Can retry the request
  3. Validation Error:

    • If no queries selected: Warning alert shown
    • Button remains disabled until valid

Summary Flow Diagram

User clicks "Start Research"
    ↓
ActionButtons.onExecute()
    ↓
IntentConfirmationPanel.handleExecute()
    ↓
ResearchInput.onExecute(selectedQueries)
    ↓
execution.executeIntentResearch(state, queries)
    ↓
[Loading State: isExecuting = true]
    ↓
intentResearchApi.executeIntentResearch(request)
    ↓
POST /api/research/intent/research
    ↓
Backend: Research Engine + Intent Analyzer
    ↓
Response: IntentDrivenResearchResponse
    ↓
[Save draft to database]
    ↓
[Cache result]
    ↓
[Loading State: isExecuting = false]
    ↓
onUpdate({ currentStep: 3 })
    ↓
StepResults Component Renders
    ↓
User sees:
  - Executive Summary
  - Direct Answer
  - Key Takeaways
  - Deliverables (Statistics, Quotes, Case Studies, Trends)
  - Sources (with links)
  - Analysis (Confidence, Gaps, Follow-ups)

Key Files Reference

  1. Frontend Components:

    • ActionButtons.tsx - Start Research button
    • IntentConfirmationPanel.tsx - Intent confirmation UI
    • ResearchInput.tsx - Step 1 component
    • StepResults.tsx - Step 3 results display
    • IntentResultsDisplay.tsx - Intent-driven results renderer
  2. Hooks:

    • useResearchExecution.ts - Research execution logic
    • useResearchWizard.ts - Wizard state management
  3. API:

    • intentResearchApi.ts - API client for research endpoints
  4. Backend:

    • handlers/intent.py - Intent research endpoint handler
    • services/research/intent/ - Intent analysis services
    • services/research/core/ - Research engine

UI States Summary

State Button Text Button State UI Feedback
Ready "Start Research" Enabled Info alert: "Ready to start research!"
No Queries "Start Research" Disabled Warning: "Please select at least one query"
Executing "Researching..." Disabled + Spinner Loading indicator
Success N/A (on Results page) N/A Results displayed in tabs
Error "Start Research" Enabled Error message displayed

This completes the code walkthrough from button click to results display! 🎉