Spaces:
Build error
A newer version of the Gradio SDK is available: 6.20.0
LangGraph Workflow State Management Fix - Design Document
Date: January 2025
Author: BeatDebate Team
Status: Draft
Review Status: Pending
1. Problem Statement
Objective: Fix the LangGraph workflow state management issue preventing discovery agent candidate scaling for follow-up queries, ensuring recently_shown_track_ids is available to the discovery agent when needed.
Current State: Follow-up queries like "More tracks by Kendrick Lamar" only return 2 tracks instead of the expected 8-10 because the discovery agent processes before recently_shown_track_ids is populated in the workflow state, preventing sophisticated candidate scaling logic from triggering.
Root Cause Identified:
recently_shown_track_idsgets populated correctly byEnhancedRecommendationService- Discovery agent processes before this data is available in the workflow state
- Discovery agent sees empty
state.recently_shown_track_ids, doesn't trigger scaling - Logs show "π― NEW QUERY: Using base 100 candidates" instead of expected "π― CANDIDATE SCALING"
Value Proposition:
- Restore Expected Behavior: Follow-up queries return 8-10 tracks as designed
- Activate Existing Logic: Enable sophisticated candidate scaling (100 β 300+ candidates for artist follow-ups)
- Improve User Experience: Users get substantial results for "more" requests
- Maintain System Integrity: Fix without breaking existing functionality
2. Goals & Non-Goals
β In Scope
- State Management Fix: Ensure
recently_shown_track_idsis available to discovery agent during workflow execution - Workflow Timing Analysis: Understand and fix the timing issue in LangGraph state preservation
- Candidate Scaling Activation: Verify existing scaling logic triggers properly for follow-up queries
- Comprehensive Testing: End-to-end testing of follow-up query scenarios
- Monitoring & Logging: Enhanced logging to track state management and scaling decisions
- Backward Compatibility: Ensure fix doesn't break existing functionality
β Out of Scope (This Fix)
- Scaling Logic Changes: The sophisticated scaling logic already exists and works correctly
- Duplicate Detection Changes: The filtering logic already works correctly
- New Features: Focus only on fixing the state management issue
- Performance Optimization: Beyond fixing the core issue
- UI/UX Changes: This is a backend workflow fix
3. Architecture Overview
Current Problematic Flow
User Query: "More tracks by Kendrick Lamar"
β
EnhancedRecommendationService.get_recommendations()
ββ populate_recently_shown_track_ids() β [10 track IDs] β
ββ LangGraph Workflow Start
β ββ Planner Agent β planning_strategy β
β ββ Discovery Agent β sees empty recently_shown_track_ids β
β β ββ Uses base 100 candidates (should be 300+) β
β ββ Judge Agent β filters duplicates correctly β
ββ Result: Only 2 tracks (should be 8-10) β
Fixed Flow (Target)
User Query: "More tracks by Kendrick Lamar"
β
EnhancedRecommendationService.get_recommendations()
ββ populate_recently_shown_track_ids() β [10 track IDs] β
ββ Initialize State with recently_shown_track_ids β
ββ LangGraph Workflow Start
β ββ Planner Agent β planning_strategy β
β ββ Discovery Agent β sees populated recently_shown_track_ids β
β β ββ Triggers scaling: 300+ candidates for artist queries β
β ββ Judge Agent β filters duplicates, returns 8-10 tracks β
ββ Result: 8-10 new tracks β
4. Technical Design
4.1 Root Cause Analysis
Current Workflow State Initialization
Location: src/services/enhanced_recommendation_service.py
Problem: State is initialized before recently_shown_track_ids is populated:
# Current problematic sequence
state = MusicRecommenderState(
user_query=query,
session_id=session_id,
# recently_shown_track_ids is empty here
)
# Later...
self.populate_recently_shown_track_ids(state, session_id) # Too late!
# Workflow starts with empty recently_shown_track_ids
workflow_result = await workflow.ainvoke(state)
Discovery Agent Scaling Logic Check
Location: src/agents/discovery/agent.py (lines 295-330)
Current Behavior:
if state.recently_shown_track_ids:
# This never triggers because list is empty
scaled_candidates = self._calculate_scaled_candidates(...)
else:
# Always uses this path
candidates = 100 # Base amount
4.2 Solution Strategy
Option A: Fix State Initialization Order (Recommended)
Approach: Populate recently_shown_track_ids BEFORE workflow initialization
Implementation:
class EnhancedRecommendationService:
async def get_recommendations(self, query: str, session_id: str) -> Dict:
# 1. First populate recently shown tracks
recently_shown = await self._get_recent_tracks_for_session(session_id)
# 2. Initialize state with populated data
state = MusicRecommenderState(
user_query=query,
session_id=session_id,
recently_shown_track_ids=recently_shown, # Pre-populated!
timestamp=time.time()
)
# 3. Start workflow with complete state
workflow_result = await workflow.ainvoke(state)
return workflow_result
Option B: State Update Before Discovery Agent
Approach: Update state in workflow before discovery agent executes
Implementation: Add a pre-discovery node to populate state:
def populate_context_node(state):
"""Populate recently_shown_track_ids before discovery agent"""
if not state.recently_shown_track_ids:
state.recently_shown_track_ids = get_recent_tracks(state.session_id)
return state
# Add to workflow graph
workflow.add_node("populate_context", populate_context_node)
workflow.add_edge("planner", "populate_context")
workflow.add_edge("populate_context", "discovery")
4.3 Recommended Solution: Option A
Rationale:
- Simpler: Fewer workflow changes
- More Efficient: No additional workflow node
- Clearer Logic: Context populated upfront
- Better Separation: Service layer handles state preparation
4.4 Implementation Details
4.4.1 Enhanced Recommendation Service Changes
File: src/services/enhanced_recommendation_service.py
Key Changes:
- Move
populate_recently_shown_track_idsbefore state initialization - Extract session tracking logic into separate method
- Add state validation before workflow execution
async def get_recommendations(self, query: str, session_id: str) -> Dict:
"""Get music recommendations with proper state management"""
try:
# 1. Prepare session context FIRST
recently_shown = await self._prepare_session_context(session_id)
# 2. Initialize state with complete context
state = MusicRecommenderState(
user_query=query,
session_id=session_id,
recently_shown_track_ids=recently_shown,
conversation_context=await self._get_conversation_context(session_id),
timestamp=time.time()
)
# 3. Validate state before workflow
self._validate_state_for_workflow(state)
# 4. Execute workflow with complete state
workflow_result = await self.workflow.ainvoke(state)
# 5. Update session after successful execution
await self._update_session_after_recommendation(session_id, workflow_result)
return workflow_result
except Exception as e:
logger.error(f"Recommendation service error: {e}")
raise
4.4.2 State Validation
def _validate_state_for_workflow(self, state: MusicRecommenderState) -> None:
"""Validate state has all required data for workflow execution"""
if not state.user_query:
raise ValueError("user_query is required")
if not state.session_id:
raise ValueError("session_id is required")
# Log state preparation for debugging
logger.info(f"π― STATE PREPARED: recently_shown={len(state.recently_shown_track_ids)} tracks")
if state.recently_shown_track_ids:
logger.info(f"π― FOLLOW-UP DETECTED: Will trigger candidate scaling")
4.4.3 Discovery Agent Logging Enhancement
File: src/agents/discovery/agent.py
Enhanced Logging:
async def execute_strategy(self, state: MusicRecommenderState) -> Dict:
"""Execute discovery strategy with enhanced state logging"""
# Log state received
logger.info(f"π― DISCOVERY AGENT STATE: recently_shown={len(state.recently_shown_track_ids)}")
if state.recently_shown_track_ids:
logger.info(f"π― CANDIDATE SCALING: Detected {len(state.recently_shown_track_ids)} previous tracks")
scaled_candidates = self._calculate_scaled_candidates(state)
logger.info(f"π― CANDIDATE SCALING: Scaling to {scaled_candidates} candidates")
else:
logger.info(f"π― NEW QUERY: Using base 100 candidates")
scaled_candidates = 100
# Continue with existing logic...
5. Implementation Plan
Phase 1: Core Fix (Week 1)
State Management Fix
- Modify
EnhancedRecommendationService.get_recommendations() - Move context population before state initialization
- Add state validation
- Modify
Enhanced Logging
- Add state preparation logging
- Enhance discovery agent scaling logs
- Add workflow state tracking
Basic Testing
- Unit tests for state preparation
- Integration tests for follow-up queries
- Manual testing with "More tracks by X" scenarios
Phase 2: Validation & Monitoring (Week 2)
Comprehensive Testing
- End-to-end follow-up query tests
- Edge case testing (empty sessions, invalid data)
- Performance impact assessment
Monitoring Enhancement
- Add metrics for candidate scaling triggers
- Track follow-up query success rates
- Monitor state management performance
Documentation Updates
- Update API documentation
- Add troubleshooting guide
- Document state management patterns
6. Testing Strategy
6.1 Unit Tests
class TestStateManagement:
async def test_recently_shown_populated_before_workflow(self):
"""Test that recently_shown_track_ids is populated before workflow starts"""
async def test_discovery_agent_receives_populated_state(self):
"""Test discovery agent sees populated recently_shown_track_ids"""
async def test_candidate_scaling_triggers_correctly(self):
"""Test candidate scaling logic triggers for follow-up queries"""
6.2 Integration Tests
async def test_followup_query_full_workflow():
"""Test complete follow-up query workflow"""
# 1. First query
result1 = await service.get_recommendations("Music by Kendrick Lamar", session_id)
assert len(result1['tracks']) == 10
# 2. Follow-up query
result2 = await service.get_recommendations("More tracks by Kendrick Lamar", session_id)
assert len(result2['tracks']) >= 8 # Should get substantial results
# 3. Verify no duplicates
track_ids_1 = {track['id'] for track in result1['tracks']}
track_ids_2 = {track['id'] for track in result2['tracks']}
assert track_ids_1.isdisjoint(track_ids_2)
6.3 Manual Testing Scenarios
- Basic Follow-up: "Music by X" β "More tracks by X"
- Cross-Artist: "Music by X" β "More tracks by Y"
- Session Boundary: Test session expiry and reset
- Error Handling: Invalid session IDs, network failures
7. Success Metrics
7.1 Primary Metrics
- Follow-up Query Success Rate: >95% return 8+ tracks
- Candidate Scaling Trigger Rate: >90% of follow-up queries trigger scaling
- Duplicate Rate: <5% duplicates in follow-up queries
- Response Time Impact: <10% increase in processing time
7.2 Quality Metrics
- User Satisfaction: Follow-up queries feel substantial and relevant
- System Reliability: No workflow failures due to state issues
- Log Clarity: Clear visibility into scaling decisions
- Maintainability: Code remains clean and testable
8. Risk Assessment
8.1 Technical Risks
Risk: State initialization order changes break existing functionality
Mitigation: Comprehensive testing of all query types, not just follow-ups
Risk: Performance impact from additional state preparation
Mitigation: Profile state preparation performance, optimize if needed
Risk: Complex edge cases with session management
Mitigation: Extensive edge case testing, graceful error handling
8.2 Implementation Risks
Risk: Breaking changes during active development
Mitigation: Feature branch development, thorough testing before merge
Risk: Incomplete understanding of LangGraph state management
Mitigation: Deep dive into LangGraph documentation, incremental changes
9. Future Considerations
9.1 State Management Patterns
- Standardized State Preparation: Create reusable patterns for state initialization
- State Validation Framework: Comprehensive validation for all workflow states
- Context Management: Enhanced conversation context handling
9.2 Monitoring & Analytics
- State Management Metrics: Track state preparation success rates
- Workflow Performance: Monitor impact of state management changes
- User Experience: Track follow-up query satisfaction
9.3 Extensibility
- Multi-Context Support: Support for multiple context types
- Dynamic State Updates: Runtime state updates during workflow execution
- Context Persistence: Enhanced session context management
10. Conclusion
This design addresses the core issue preventing follow-up queries from returning substantial results. By fixing the state management timing issue, we can activate the sophisticated candidate scaling logic that already exists in the system.
The solution is focused, low-risk, and maintains backward compatibility while significantly improving the user experience for follow-up queries. The enhanced logging and monitoring will provide clear visibility into the fix's effectiveness.
Expected Outcome: Follow-up queries like "More tracks by Kendrick Lamar" will consistently return 8-10 new tracks instead of just 2, providing users with the substantial results they expect from the BeatDebate system.
Document Version: 1.0
Created: January 2025
Status: Design Phase
Next Phase: Implementation Planning