Spaces:
Build error
Build error
Add autopep8 as a dependency for code formatting - Updated pyproject.toml to include autopep8 version 2.3.2 for improved code style consistency. Modified uv.lock to reflect the new dependency and its requirements. This update aims to enhance code maintainability and adherence to style guidelines.
f7943d1 A newer version of the Gradio SDK is available: 6.19.0
Context-Aware Planner Agent Fix
Problem Statement
Follow-up queries like "More tracks" are being incorrectly classified as discovery queries instead of respecting conversation context, resulting in irrelevant recommendations.
Root Cause: The planner agent ignores context overrides and always runs fresh query understanding on raw queries, even when the conversation context service has correctly identified them as follow-ups with preserved entities and intent.
Impact:
- Users get random discovery tracks (mostly Radiohead) instead of more Michael Jackson R&B tracks
- Broken conversation flow where "More tracks" doesn't build on previous queries
- Poor user experience with conversational music discovery
Success Criteria:
- "More tracks" after "Songs by Michael Jackson that are R&B" returns more Michael Jackson R&B tracks
- Fresh queries still work normally through query understanding
- No regression in existing functionality
Current Architecture Analysis
Current Flow (Broken)
- Context Analyzer β correctly identifies "More tracks" as follow-up, creates context override
- Planner Agent β ignores context override, runs fresh query understanding on "More tracks"
- Query Understanding β returns
intent: 'discovery',entities: {artists: [], genres: []} - Discovery Agent β receives context override, tries to patch entities after wrong planning
Log Evidence
Line 1363: π§ LLM RAW RESPONSE: {'intent': 'discovery', 'musical_entities': {'artists': [], 'genres': [], 'tracks': [], 'moods': []}
Line 1376: Converted QueryUnderstanding to entities [artists_count=0, genres_count=0, intent=discovery]
Line 1393: π§ DEBUG: context_override data: {'intent_override': 'hybrid_artist_genre', 'target_entity': 'Michael Jackson', 'required_genres': ['R&B']}
Solution Design
New Flow (Fixed)
- Context Analyzer β correctly identifies follow-up, creates context override
- Planner Agent β checks for context override before query understanding
- Context Override Path β uses preserved entities and intent from context
- Fresh Query Path β runs normal query understanding for new queries
Architecture Changes
1. Context-Aware Planner Agent
class PlannerAgent(BaseAgent):
async def process(self, state: MusicRecommenderState) -> MusicRecommenderState:
"""Process user query with context-awareness."""
try:
self.logger.info("Starting planner agent processing")
# π NEW: Check for context override before query understanding
if hasattr(state, 'context_override') and state.context_override:
context_override = state.context_override
if self._is_followup_with_preserved_context(context_override):
self.logger.info("Context override detected, using preserved entities and intent")
# Use preserved context instead of fresh query understanding
query_understanding = self._create_understanding_from_context(
state.user_query, context_override
)
entities = self._create_entities_from_context(context_override)
state.query_understanding = query_understanding
state.entities = entities
# Mark that we used context override
self._context_override_applied = True
else:
# Context override exists but not a preserved context case
query_understanding = await self._understand_user_query(state.user_query)
entities = self._convert_understanding_to_entities(query_understanding)
state.query_understanding = query_understanding
state.entities = entities
else:
# No context override - fresh query understanding
query_understanding = await self._understand_user_query(state.user_query)
entities = self._convert_understanding_to_entities(query_understanding)
state.query_understanding = query_understanding
state.entities = entities
# Continue with rest of planner logic (task analysis, strategy creation, etc.)
# ... existing code ...
except Exception as e:
# ... existing error handling ...
2. Context Override Detection
def _is_followup_with_preserved_context(self, context_override: Dict) -> bool:
"""
Check if context override contains preserved entities that should skip query understanding.
Returns True for follow-ups with preserved entities like:
- Artist deep dives with preserved genres
- Style continuations with preserved context
- Hybrid queries with preserved filters
"""
if not isinstance(context_override, dict):
return False
# Check for follow-up indicators
is_followup = context_override.get('is_followup', False)
has_preserved_entities = 'preserved_entities' in context_override
has_intent_override = 'intent_override' in context_override
# Specific follow-up types that need preserved context
followup_types_with_context = [
'hybrid_artist_genre', # "More tracks" after "Songs by X that are Y"
'artist_style_refinement', # "More jazzy tracks" after artist query
'style_continuation' # Continue with same style/mood
]
intent_override = context_override.get('intent_override')
return (is_followup and
has_preserved_entities and
has_intent_override and
intent_override in followup_types_with_context)
3. Context-to-Entities Conversion
def _create_understanding_from_context(self, user_query: str, context_override: Dict) -> QueryUnderstanding:
"""Create QueryUnderstanding from preserved context override."""
preserved_entities = context_override.get('preserved_entities', {})
intent_override = context_override.get('intent_override', 'discovery')
confidence = context_override.get('confidence', 0.9)
# Extract preserved entities
artists = self._extract_entity_names(preserved_entities.get('artists', {}).get('primary', []))
genres = self._extract_entity_names(preserved_entities.get('genres', {}).get('primary', []))
moods = self._extract_entity_names(preserved_entities.get('moods', {}).get('primary', []))
# Map intent override to QueryIntent enum
intent_mapping = {
'hybrid_artist_genre': QueryIntent.HYBRID,
'artist_style_refinement': QueryIntent.HYBRID,
'style_continuation': QueryIntent.GENRE_MOOD,
'artist_deep_dive': QueryIntent.ARTIST_SIMILARITY
}
intent = intent_mapping.get(intent_override, QueryIntent.DISCOVERY)
self.logger.info(
f"π― Created understanding from context: intent={intent.value}, "
f"artists={artists}, genres={genres}, confidence={confidence}"
)
return QueryUnderstanding(
intent=intent,
confidence=confidence,
artists=artists,
genres=genres,
moods=moods,
activities=[],
original_query=user_query,
normalized_query=user_query.lower(),
reasoning=f"Context override: {intent_override} follow-up with preserved entities"
)
def _create_entities_from_context(self, context_override: Dict) -> Dict[str, Any]:
"""Create entities structure from context override."""
preserved_entities = context_override.get('preserved_entities', {})
# Extract preserved entity data
artists_data = preserved_entities.get('artists', {})
genres_data = preserved_entities.get('genres', {})
moods_data = preserved_entities.get('moods', {})
# Convert to proper entities structure
entities = {
"musical_entities": {
"artists": {
"primary": self._extract_entity_names(artists_data.get('primary', [])),
"similar_to": []
},
"genres": {
"primary": self._extract_entity_names(genres_data.get('primary', [])),
"secondary": []
},
"tracks": {
"primary": [],
"referenced": []
},
"moods": {
"primary": self._extract_entity_names(moods_data.get('primary', [])),
"energy": [],
"emotion": []
}
},
"contextual_entities": {
"activities": {
"physical": [],
"mental": [],
"social": []
},
"temporal": {
"decades": [],
"periods": []
}
},
"confidence_scores": {
"overall": context_override.get('confidence', 0.9)
},
"extraction_method": "context_override_preserved",
"intent_analysis": {
"intent": context_override.get('intent_override', 'hybrid'),
"confidence": context_override.get('confidence', 0.9),
"context_override_applied": True
}
}
self.logger.info(
f"π― Created entities from context: "
f"artists={len(entities['musical_entities']['artists']['primary'])}, "
f"genres={len(entities['musical_entities']['genres']['primary'])}"
)
return entities
def _extract_entity_names(self, entity_list: List) -> List[str]:
"""Extract names from entity list that may contain dicts or strings."""
names = []
for item in entity_list:
if isinstance(item, dict):
names.append(item.get('name', str(item)))
elif isinstance(item, str):
names.append(item)
else:
names.append(str(item))
return names
Implementation Plan
Phase 1: Core Context Detection
- Add context override detection to planner agent
- Implement preserved entity extraction
- Add logging for debugging context usage
Phase 2: Context-to-Entities Conversion
- Implement understanding creation from context
- Implement entities creation from context
- Handle intent mapping from context override
Phase 3: Integration & Testing
- Test with "More tracks" scenario
- Verify backward compatibility with fresh queries
- Test edge cases (malformed context overrides)
Phase 4: Monitoring & Validation
- Add metrics for context override usage
- Monitor recommendation quality
- Validate conversation flow improvements
Testing Strategy
Test Cases
1. Follow-up Query Success
Query 1: "Songs by Michael Jackson that are R&B"
Expected: Michael Jackson R&B tracks returned
Query 2: "More tracks"
Expected: More Michael Jackson R&B tracks (not random discovery)
2. Fresh Query Compatibility
Query: "Electronic music for studying"
Expected: Normal query understanding, genre/mood-based recommendations
3. Edge Cases
- Context override with missing preserved_entities
- Malformed intent_override values
- Context override with empty entities
Success Metrics
- Follow-up queries maintain conversation context (95%+ success rate)
- Fresh queries unaffected (no regression)
- User satisfaction with "More tracks" functionality
Risk Mitigation
Backward Compatibility
- Fresh queries (no context override) use existing query understanding path
- Malformed context overrides fall back to normal query understanding
- All existing planner functionality preserved
Error Handling
- Invalid context overrides log warnings but don't break workflow
- Missing entities in context override fall back to empty entities
- Intent mapping failures default to DISCOVERY intent
Performance
- Context override path is faster (skips LLM query understanding)
- No additional API calls or LLM overhead for follow-ups
- Minimal code complexity increase
Metrics & Monitoring
Key Metrics
- Context Override Usage Rate: % of queries using context override
- Follow-up Success Rate: % of follow-ups maintaining conversation context
- Query Understanding Accuracy: Fresh vs context-override query accuracy
- User Engagement: Session length and follow-up query frequency
Logging Enhancements
- Log when context override is detected and used
- Log preserved entities and intent mappings
- Log fallback cases for debugging
Dependencies
No External Dependencies
- Uses existing context override structure
- Uses existing QueryUnderstanding and entities formats
- No new API calls or services required
Internal Dependencies
- Enhanced recommendation service context override creation
- Existing conversation context service functionality
- Existing planner agent query understanding flow
Deployment Strategy
Rollout Plan
- Dev Testing: Implement and test in development environment
- A/B Testing: Split traffic to compare context-aware vs original planner
- Gradual Rollout: Start with 10% of follow-up queries, monitor metrics
- Full Deployment: Roll out to 100% after validation
Rollback Plan
- Feature flag to disable context override detection
- Fallback to original query understanding for all queries
- No database or API changes required for rollback
Technical Implementation Notes
Code Structure
- New methods added to existing
PlannerAgentclass - No breaking changes to existing interfaces
- Context override detection is additive functionality
Testing Requirements
- Unit tests for context override detection
- Integration tests for end-to-end follow-up flow
- Performance tests to ensure no regression
Documentation Updates
- Update planner agent documentation with context-aware behavior
- Add troubleshooting guide for context override issues
- Update system architecture diagrams