Add podcast research metadata mapping and summary sections

This commit is contained in:
ي
2026-04-19 16:51:51 +05:30
parent 280159669b
commit 95edd7d470
3 changed files with 89 additions and 0 deletions

View File

@@ -218,6 +218,9 @@ Listener CTA: {request.analysis.get('listener_cta', 'N/A')}
summary = "" summary = ""
key_insights = [] key_insights = []
expert_quotes = []
listener_cta_suggestions = []
mapped_angles = []
if raw_content and sources: if raw_content and sources:
logger.warning(f"[Podcast Research] Extracting insights from {len(sources)} sources for user {user_id}") logger.warning(f"[Podcast Research] Extracting insights from {len(sources)} sources for user {user_id}")
@@ -337,13 +340,22 @@ QUALITY STANDARDS:
try: try:
summary = data.get("summary", "") summary = data.get("summary", "")
key_insights = [PodcastResearchInsight(**insight) for insight in data.get("key_insights", [])] key_insights = [PodcastResearchInsight(**insight) for insight in data.get("key_insights", [])]
expert_quotes = data.get("expert_quotes", [])
listener_cta_suggestions = data.get("listener_cta_suggestions", [])
mapped_angles = data.get("mapped_angles", [])
except Exception as insight_err: except Exception as insight_err:
logger.warning(f"[Podcast Research] Failed to parse insights: {insight_err}. Data keys: {list(data.keys()) if isinstance(data, dict) else 'not a dict'}") logger.warning(f"[Podcast Research] Failed to parse insights: {insight_err}. Data keys: {list(data.keys()) if isinstance(data, dict) else 'not a dict'}")
summary = data.get("summary", "") if isinstance(data, dict) else "" summary = data.get("summary", "") if isinstance(data, dict) else ""
key_insights = [] key_insights = []
expert_quotes = data.get("expert_quotes", []) if isinstance(data, dict) else []
listener_cta_suggestions = data.get("listener_cta_suggestions", []) if isinstance(data, dict) else []
mapped_angles = data.get("mapped_angles", []) if isinstance(data, dict) else []
else: else:
summary = "" summary = ""
key_insights = [] key_insights = []
expert_quotes = []
listener_cta_suggestions = []
mapped_angles = []
except HTTPException: except HTTPException:
raise raise
except Exception as exc: except Exception as exc:
@@ -423,5 +435,8 @@ QUALITY STANDARDS:
search_type=result.get("search_type") if isinstance(result, dict) else None, search_type=result.get("search_type") if isinstance(result, dict) else None,
provider=result.get("provider", "exa") if isinstance(result, dict) else "exa", provider=result.get("provider", "exa") if isinstance(result, dict) else "exa",
content=raw_content, content=raw_content,
mapped_angles=mapped_angles,
expert_quotes=expert_quotes,
listener_cta_suggestions=listener_cta_suggestions,
estimate=estimate, estimate=estimate,
) )

View File

@@ -285,6 +285,78 @@ export const ResearchSummary: React.FC<ResearchSummaryProps> = ({
) )
)} )}
{/* Expert Quotes */}
<Box sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ mb: 1.5, color: "#0f172a", fontWeight: 700 }}>
Expert Quotes
</Typography>
{research.expertQuotes && research.expertQuotes.length > 0 ? (
<Stack spacing={1}>
{research.expertQuotes.slice(0, 4).map((quote, idx) => (
<Paper key={`${quote.source_index}-${idx}`} elevation={0} sx={{ p: 1.5, border: "1px solid rgba(0,0,0,0.06)", borderRadius: 2 }}>
<Typography variant="body2" sx={{ color: "#334155", lineHeight: 1.55 }}>
{quote.quote}
</Typography>
<Typography variant="caption" sx={{ color: "#64748b", display: "block", mt: 0.5 }}>
Source S{quote.source_index}
</Typography>
</Paper>
))}
</Stack>
) : (
<Typography variant="body2" sx={{ color: "#64748b" }}>
No expert quotes extracted yet.
</Typography>
)}
</Box>
{/* Listener CTAs */}
<Box sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ mb: 1.5, color: "#0f172a", fontWeight: 700 }}>
Listener CTAs
</Typography>
{research.listenerCta && research.listenerCta.length > 0 ? (
<Stack spacing={1}>
{research.listenerCta.slice(0, 4).map((cta, idx) => (
<Paper key={`cta-${idx}`} elevation={0} sx={{ p: 1.5, border: "1px solid rgba(0,0,0,0.06)", borderRadius: 2 }}>
<Typography variant="body2" sx={{ color: "#334155", lineHeight: 1.55 }}>
{cta}
</Typography>
</Paper>
))}
</Stack>
) : (
<Typography variant="body2" sx={{ color: "#64748b" }}>
No listener CTAs suggested yet.
</Typography>
)}
</Box>
{/* Mapped Angles */}
<Box sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ mb: 1.5, color: "#0f172a", fontWeight: 700 }}>
Mapped Angles
</Typography>
{research.mappedAngles && research.mappedAngles.length > 0 ? (
<Stack spacing={1}>
{research.mappedAngles.slice(0, 4).map((angle, idx) => (
<Paper key={`angle-${idx}`} elevation={0} sx={{ p: 1.5, border: "1px solid rgba(0,0,0,0.06)", borderRadius: 2 }}>
<Typography variant="subtitle2" sx={{ color: "#0f172a", fontWeight: 700, mb: 0.5 }}>
{angle.title || `Angle ${idx + 1}`}
</Typography>
<Typography variant="body2" sx={{ color: "#334155", lineHeight: 1.55 }}>
{angle.why || "No rationale provided."}
</Typography>
</Paper>
))}
</Stack>
) : (
<Typography variant="body2" sx={{ color: "#64748b" }}>
No mapped angles available yet.
</Typography>
)}
</Box>
{/* Search Queries Used */} {/* Search Queries Used */}
{research.searchQueries && research.searchQueries.length > 0 && ( {research.searchQueries && research.searchQueries.length > 0 && (
<Box sx={{ mt: 4, pt: 3, borderTop: "1px solid rgba(0,0,0,0.04)" }}> <Box sx={{ mt: 4, pt: 3, borderTop: "1px solid rgba(0,0,0,0.04)" }}>

View File

@@ -198,6 +198,8 @@ const mapExaResearchResponse = (response: any): Research => {
source_indices: insight.source_indices || [] source_indices: insight.source_indices || []
})); }));
// Backend keys must match PodcastExaResearchResponse exactly:
// expert_quotes, listener_cta_suggestions, mapped_angles
const expertQuotes = (response.expert_quotes || []).map((eq: any) => ({ const expertQuotes = (response.expert_quotes || []).map((eq: any) => ({
quote: eq.quote || eq.text || "", quote: eq.quote || eq.text || "",
source_index: eq.source_index ?? 0 source_index: eq.source_index ?? 0