File size: 6,592 Bytes
a559920
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import json
from tools.playwright_tool import playwright_web_read 
from prompts.agent_prompts import final_summarizer_prompt
from agents import Agent, Runner, trace
from dotenv import load_dotenv
import os

load_dotenv(override=True)
default_model_name = os.environ.get('DEFAULT_MODEL_NAME')

final_report_agent = Agent(
    name="Final Report Agent",
    instructions=final_summarizer_prompt,
    tools=[playwright_web_read],   # so it can open a few URLs if needed
    model=default_model_name
)

async def generate_final_report(framework: str, topic: str, section_results: dict, trace_id: str, trace_name: str) -> dict:
    """
    Generate comprehensive final report with fact deduplication and framework-specific structure.
    
    Args:
        framework: "big-idea" or "specific-idea" 
        topic: research topic/idea
        section_results: dict of section_name -> {section_brief, artifacts}
    
    Returns:
        dict with structured_summary (JSON) and narrative_report (text)
    """
    
    # Build structured summary for JSON display
    merged_summary = {
        "framework": framework,
        "topic_or_idea": topic,
        "sections": {
            s: {
                "highlights": section_results[s]["section_brief"].get("highlights", []),
                "confidence": section_results[s]["section_brief"].get("confidence", 0.0),
                "facts_ref": section_results[s]["section_brief"].get("facts_ref", []),
                "gaps_next": section_results[s]["section_brief"].get("gaps_next", []),
            }
            for s in section_results
        }
    }
    
    # Collect section analyses
    section_analyses = [
        res["artifacts"]["analysis"] for res in section_results.values()
    ]
    
    # Deduplicate facts across sections (handling fact_id collisions)
    all_facts = []
    seen_claims = set()
    global_facts_to_url_mapping = {}
    fact_id_counter = 1
    
    for section_name, res in section_results.items():
        facts = res["artifacts"]["facts"].get("facts", [])
        section_facts_mapping = res["artifacts"].get("facts_to_url_mapping", {})
        
        for fact in facts:
            # Create unique key for deduplication based on content
            entity = fact.get('entity', '').strip().lower()
            claim = fact.get('claim', '').strip().lower()
            source_url = fact.get('source_url', '')
            fact_key = f"{entity}|{claim}|{source_url}"
            
            if fact_key not in seen_claims:
                # Create new global fact_id to avoid collisions
                new_fact_id = f"global_{fact_id_counter}"
                fact_id_counter += 1
                
                # Update fact with new global ID
                fact_copy = fact.copy()
                old_fact_id = fact.get('fact_id')
                fact_copy['fact_id'] = new_fact_id
                fact_copy['section_source'] = section_name  # Track which section this came from
                
                all_facts.append(fact_copy)
                seen_claims.add(fact_key)
                
                # Map the new global fact_id to URLs, preserving old mapping
                if old_fact_id in section_facts_mapping:
                    global_facts_to_url_mapping[new_fact_id] = section_facts_mapping[old_fact_id]
    
    # Create section-specific facts mapping (keeping original IDs for section references)
    facts_to_url_mapping = {
        s: res["artifacts"].get("facts_to_url_mapping", {})
        for s,res in section_results.items()
    }
    
    # Extract section confidences
    section_confidences = {
        s: res["section_brief"].get("confidence", 0.0)
        for s,res in section_results.items()
    }

    # Set framework-specific reporting structure
    if framework == "big-idea":
        report_structure = "a market landscape analysis"
        narrative_structure = """
**Foundation**: What is this domain and why does it matter? Establish the basic concept, scale, and relevance.

**Current Landscape**: Who are the key players and what's happening now? Introduce major companies, technologies, and current market activity with concrete examples.

**Market Dynamics**: How does this ecosystem work? Explore business models, adoption patterns, competitive dynamics, and value chains.

**Critical Insights**: What are the non-obvious patterns and tensions? Synthesize findings to reveal underlying trends, contradictions, and emerging opportunities.

**Strategic Implications**: What does this mean for stakeholders? Distill insights into actionable understanding and key considerations for decision-makers.
"""
    else:  # specific-idea
        report_structure = "a business viability assessment" 
        narrative_structure = """
**Foundation**: What problem does this solve and for whom? Establish the core pain point, affected stakeholders, and why this matters now.

**Problem-Solution Fit**: How real and urgent is this problem? Present evidence of customer pain, market size, and demand signals with specific examples.

**Competitive Reality**: What's the competitive context? Analyze existing solutions, differentiation opportunities, and defensive positioning.

**Business Viability**: How could this work as a business? Explore revenue models, go-to-market approaches, and buyer dynamics.

**Strategic Assessment**: What are the key risks and next steps? Synthesize insights into execution priorities and critical uncertainties to resolve.
"""

    # Prepare payload for final report agent
    payload = {
        "framework": framework,
        "topic_or_idea": topic,
        "report_structure": report_structure,
        "narrative_structure": narrative_structure,
        "section_analyses": section_analyses,
        "facts_to_url_mapping": facts_to_url_mapping,
        "all_facts": all_facts,
        "section_confidences": section_confidences,
        "global_facts_to_url_mapping": global_facts_to_url_mapping
    }

    with trace(f"{trace_name} trace", trace_id=trace_id):
        # Generate final narrative report
        final_report = await Runner.run(final_report_agent, [
            {"role": "user", "content": json.dumps(payload, ensure_ascii=False)}
        ])

    return {
        "structured_summary": merged_summary,
        "narrative_report": final_report.final_output,
        "metadata": {
            "total_facts": len(all_facts),
            "avg_confidence": sum(section_confidences.values()) / len(section_confidences) if section_confidences else 0,
            "sections_count": len(section_results)
        }
    }