this is my base class ,

from utils.pdf_utils import parse_topics

class BaseAgent:
    def __init__(self, client, is_azure):
        self.client = client
        self.is_azure = is_azure

    def analyze_document(self, prompt_system, prompt_user, text):
        if self.is_azure:
            messages = [
                {"role": "system", "content": prompt_system},
                {"role": "user", "content": prompt_user + "\n\n" + text},
            ]
            response = self.client.generate(messages=messages, temperature=0)
            raw_text = response.choices[0].message.content.strip()
        else:
            raw_text = self.client.generate(prompt_system, prompt_user + "\n\n" + text)
        return parse_topics(raw_text)

    def generate_consolidated_summary_from_topics(self, prompt_system, prompt_user_template, processed_files):
        topics_block = ""
        for filename, filedata in processed_files.items():
            topics_block += f"### Topics from document: {filename}\n"
            for topic in sorted(filedata["topics"], key=lambda x: x["score"], reverse=True):
                topics_block += f"- **{topic['name']}**: {topic['summary']} (Relevance: {topic['score']})\n"
            topics_block += "\n"

        prompt_user = prompt_user_template.format(topics_block=topics_block)

        if self.is_azure:
            messages = [
                {"role": "system", "content": prompt_system},
                {"role": "user", "content": prompt_user},
            ]
            response = self.client.generate(messages=messages, temperature=0)
            return response.choices[0].message.content.strip()
        else:
            return self.client.generate(prompt_system, prompt_user)

    def generate_topic_detail(self, prompt_system, prompt_user_template, document_text, topic):
        prompt_user = prompt_user_template.format(text=document_text, topic=topic)
        if self.is_azure:
            messages = [
                {"role": "system", "content": prompt_system},
                {"role": "user", "content": prompt_user},
            ]
            response = self.client.generate(messages=messages, temperature=0, stream=True)
            
            collected_chunks = []
            for chunk in response:
                if hasattr(chunk, "choices") and chunk.choices:
                    delta = chunk.choices[0].delta
                    content = getattr(delta, "content", "")
                    if content:
                        collected_chunks.append(content)
                        yield "".join(collected_chunks) + "▌"
            
            # Final yield without cursor
            if collected_chunks:
                yield "".join(collected_chunks)
        else:
            # For Gemini, we'll simulate streaming with 3 chunks
            full_response = self.client.generate(prompt_system, prompt_user)
            chunk_size = max(len(full_response) // 3, 1)
            collected = ""
            for i in range(0, len(full_response), chunk_size):
                collected = full_response[:i+chunk_size]
                yield collected + "▌"
            yield collected  # Final yield


we want the consolidate summary also came as streamline as topics 

below is my main.py 
import streamlit as st
from io import BytesIO
from datetime import datetime

from utils.pdf_utils import extract_text
from clients.azure_client import AzureClient
from clients.gemini_client import GeminiClient

from agents.tariff_agent import TariffTradeAgent
from agents.sales_agent import SalesAgent
from agents.supply_chain_agent import SupplyChainAgent
from agents.overall_agent import OverallAgent
from agents.enhanced_chat_agent import EnhancedChatAgent

def analyze_documents(mode, agent, processed_files, sess):
    for filename, file_bytes in sess['uploaded_files'].items():
        if filename not in processed_files:
            try:
                file_obj = BytesIO(file_bytes)
                file_obj.name = filename
                text = extract_text(file_obj)
                if text:
                    topics = agent.analyze_document(text)
                    processed_files[filename] = {
                        "text": text,
                        "topics": topics,
                        "context": text,
                    }
                    st.toast(f"✅ Processed {filename}")
                else:
                    st.error(f"❌ Failed to extract text for {filename}")
            except Exception as e:
                st.error(f"Error processing {filename}: {str(e)}")

    with st.spinner(f"Generating consolidated summary for {mode}..."):
        summary = agent.generate_consolidated_summary_from_topics(processed_files)
        if mode == "overall":
            sess['consolidated_summary_overall'] = summary
        elif mode == "tariff":
            sess['consolidated_summary_tariff'] = summary
        elif mode == "sales":
            sess['consolidated_summary_sales'] = summary
        elif mode == "supplychain":
            sess['consolidated_summary_supplychain'] = summary

    sess['last_analysis_mode'] = mode
    sess['active_file'] = None
    sess['active_topic'] = None
    st.success(f"Analysis and summary generated for {mode}.")

def main():
    st.set_page_config(page_title="Macy's Document Analysis Pro", layout="wide")
    st.title("📄 Macy's Document Analysis & Chat Tool")

    # Mode selection (Azure or Gemini)
    provider_select = st.radio("Select AI Provider", ["Azure GPT", "Google Gemini"])

    # Session Initialization
    if "session" not in st.session_state:
        st.session_state.session = {
            'uploaded_files': {},
            'processed_files_overall': {},
            'processed_files_tariff': {},
            'processed_files_sales': {},
            'processed_files_supplychain': {},
            'active_file': None,
            'active_topic': None,
            'topic_details_cache': {},
            'chat_history': [],
            'consolidated_summary_overall': None,
            'consolidated_summary_tariff': None,
            'consolidated_summary_sales': None,
            'consolidated_summary_supplychain': None,
            'last_analysis_mode': None
        }

    if "client" not in st.session_state or st.session_state.get("provider") != provider_select:
        # Initialize clients and agents based on selection
        if provider_select == "Azure GPT":
            st.session_state.client = AzureClient()
            is_azure = True
        else:
            st.session_state.client = GeminiClient()
            is_azure = False

        # Setup agents
        st.session_state.agent_overall = OverallAgent(st.session_state.client, is_azure)
        st.session_state.agent_tariff = TariffTradeAgent(st.session_state.client, is_azure)
        st.session_state.agent_sales = SalesAgent(st.session_state.client, is_azure)
        st.session_state.agent_supplychain = SupplyChainAgent(st.session_state.client, is_azure)
        st.session_state.chat_agent = EnhancedChatAgent(st.session_state.client, is_azure)
        st.session_state.provider = provider_select

    sess = st.session_state.session

    # --- File Upload Section ---
    st.subheader("1. Upload Documents")
    uploaded_files = st.file_uploader(
        "Upload Macy's earnings transcript PDFs",
        type=["pdf"],
        accept_multiple_files=True,
        key="file_uploader",
    )
    if uploaded_files:
        new_files_added = False
        for file in uploaded_files:
            if file.name not in sess['uploaded_files']:
                sess['uploaded_files'][file.name] = file.getvalue()
                new_files_added = True

        # Automatically trigger overall analysis on new uploads
        if new_files_added:
            analyze_documents(
                mode="overall",
                agent=st.session_state.agent_overall,
                processed_files=sess['processed_files_overall'],
                sess=sess
            )

    # --- Analysis Buttons Section ---
    st.subheader("2. Analyze Documents")
    col0, col1, col2, col3 = st.columns(4)
    analyze_overall = col0.button("📑 Overall Summary")
    analyze_tariff = col1.button("🔍 Analyze Tariffs & Trade")
    analyze_sales = col2.button("📈 Analyze Sales & Consumer Behavior")
    analyze_supplychain = col3.button("🚚 Analyze Supply Chain & Logistics")

    mode = None
    agent = None
    processed_files = None

    if analyze_overall or analyze_tariff or analyze_sales or analyze_supplychain:
        if not sess['uploaded_files']:
            st.error("Please upload documents before analyzing.")
            st.stop()

        if analyze_overall:
            mode = "overall"
            agent = st.session_state.agent_overall
            processed_files = sess['processed_files_overall']
        elif analyze_tariff:
            mode = "tariff"
            agent = st.session_state.agent_tariff
            processed_files = sess['processed_files_tariff']
        elif analyze_sales:
            mode = "sales"
            agent = st.session_state.agent_sales
            processed_files = sess['processed_files_sales']
        elif analyze_supplychain:
            mode = "supplychain"
            agent = st.session_state.agent_supplychain
            processed_files = sess['processed_files_supplychain']

        analyze_documents(mode, agent, processed_files, sess)

    # --- Show consolidated summary and topics ---
    last_mode = sess['last_analysis_mode']
    if last_mode is None:
        st.info("Select an analysis type above to get started.")
        return

    if last_mode == "overall":
        processed_files = sess['processed_files_overall']
        consolidated_summary = sess['consolidated_summary_overall']
        sidebar_header = "Overall Topics"
        main_header = "🌟 Consolidated Executive Summary"
        agent = st.session_state.agent_overall
    elif last_mode == "tariff":
        processed_files = sess['processed_files_tariff']
        consolidated_summary = sess['consolidated_summary_tariff']
        sidebar_header = "Tariff Topics"
        main_header = "🌟 Consolidated Tariff & Trade Summary"
        agent = st.session_state.agent_tariff
    elif last_mode == "sales":
        processed_files = sess['processed_files_sales']
        consolidated_summary = sess['consolidated_summary_sales']
        sidebar_header = "Sales Topics"
        main_header = "🌟 Consolidated Sales & Consumer Behavior Summary"
        agent = st.session_state.agent_sales
    else:  # supplychain
        processed_files = sess['processed_files_supplychain']
        consolidated_summary = sess['consolidated_summary_supplychain']
        sidebar_header = "Supply Chain Topics"
        main_header = "🌟 Consolidated Supply Chain & Logistics Summary"
        agent = st.session_state.agent_supplychain

    # --- Display Topics in Sidebar ---
    st.sidebar.header(sidebar_header)

    if last_mode == "overall":
        # Aggregate topics across all documents
        topics_by_file = {}
        for filename, filedata in processed_files.items():
            for topic in filedata["topics"]:
                topics_by_file.setdefault(topic['name'], []).append({
                    'file': filename,
                    'score': topic['score']
                })

        # Display aggregated topics
        for topic_name, occurrences in sorted(topics_by_file.items(),
                                           key=lambda x: max(x[1], key=lambda y: y['score'])['score'],
                                           reverse=True):
            total_score = sum(occ['score'] for occ in occurrences)
            file_count = len(occurrences)
            btn_label = f"{topic_name} (📈 {total_score:.1f} | 📑 {file_count})"

            if st.sidebar.button(
                btn_label,
                key=f"overall_topic_{topic_name}",
                use_container_width=True,
            ):
                sess['active_topic'] = topic_name
                sess['active_file'] = None  # Clear active file when selecting topic cross-doc
    else:
        # Original per-file topic display
        for filename, filedata in processed_files.items():
            expanded = (filename == sess['active_file'])
            with st.sidebar.expander(f"📄 {filename}", expanded=expanded):
                topics = sorted(filedata["topics"], key=lambda t: t["score"], reverse=True)
                for topic in topics:
                    is_selected = (
                        sess['active_file'] == filename and sess['active_topic'] == topic['name']
                    )
                    style = "primary" if is_selected else "secondary"

                    if st.button(
                        f"• {topic['name']} (Score: {topic['score']})",
                        key=f"{last_mode}_topic_{filename}_{topic['name']}",
                        type=style,
                        use_container_width=True,
                    ):
                        sess['active_file'] = filename
                        sess['active_topic'] = topic['name']

    # --- DETAILED TOPIC ANALYSIS ---
    st.subheader("Detailed Topic Analysis")
    if last_mode == "overall" and sess['active_topic']:
        # Cross-document topic analysis
        st.subheader(f"Cross-Document Analysis: {sess['active_topic']}")

        # Collect all relevant mentions
        mentions = []
        for filename, filedata in processed_files.items():
            for topic in filedata["topics"]:
                if topic['name'] == sess['active_topic']:
                    mentions.append({
                        'file': filename,
                        'summary': topic.get('summary', ''),
                        'score': topic['score'],
                        'context': filedata['context']
                    })

        # Display prevalence metrics
        with st.expander("📊 Topic Prevalence Across Documents"):
            cols = st.columns(3)
            cols[0].metric("Total Documents", len(mentions))
            avg_relevance = (sum(m['score'] for m in mentions)/len(mentions)) if mentions else 0
            cols[1].metric("Average Relevance", f"{avg_relevance:.1f}")
            cols[2].metric("First Mentioned", mentions[0]['file'] if mentions else "N/A")

        # Generate deep analysis
        if mentions:
            combined_context = "\n\n".join(
                f"=== {m['file']} ===\n{m['context']}"
                for m in mentions
            )
            key_cache = (last_mode, sess['active_topic'])
            if key_cache in sess['topic_details_cache']:
                st.markdown(sess['topic_details_cache'][key_cache])
            else:
                placeholder = st.empty()
                collected_text = ""

                try:
                    for chunk in agent.generate_topic_detail(combined_context, sess['active_topic']):
                        if chunk:
                            collected_text = chunk
                            if "▌" in collected_text:
                                # Streaming in progress
                                placeholder.markdown(collected_text)
                            else:
                                # Final chunk
                                placeholder.markdown(collected_text)
                                sess['topic_details_cache'][key_cache] = collected_text
                except Exception as e:
                    st.error(f"Error generating analysis: {str(e)}")
                    sess['topic_details_cache'][key_cache] = f"Error: {str(e)}"
        else:
            st.warning("No document mentions found for this topic")

    elif sess['active_file'] and sess['active_topic']:
        # Original single-document analysis
        key_cache = (last_mode, sess['active_file'], sess['active_topic'])
        if key_cache in sess['topic_details_cache']:
            st.markdown(sess['topic_details_cache'][key_cache])
        else:
            placeholder = st.empty()
            collected_text = ""

            try:
                for chunk in agent.generate_topic_detail(
                    processed_files[sess['active_file']]['context'],
                    sess['active_topic'],
                ):
                    if chunk:
                        collected_text = chunk
                        if "▌" in collected_text:
                            placeholder.markdown(collected_text)
                        else:
                            placeholder.markdown(collected_text)
                            sess['topic_details_cache'][key_cache] = collected_text
            except Exception as e:
                st.error(f"Error showing topic detail: {str(e)}")
                sess['topic_details_cache'][key_cache] = f"Error: {str(e)}"
    else:
        st.info("Select a file and topic from the sidebar to view detailed analysis.")

    st.markdown("---")

    # --- CONSOLIDATED SUMMARY ---
    st.header(main_header)
    if consolidated_summary:
        st.markdown(consolidated_summary)
    else:
        st.info(f"No consolidated summary available for {last_mode.capitalize()} yet.")

    st.divider()

    # --- Advanced Chat: Tariff & Trade Policies ---
    st.subheader("💬 Advanced Document Analysis (Tariff & Trade)")
    tariff_files = list(sess['processed_files_tariff'].keys())
    selected_files = st.multiselect("Select documents for question answering:", tariff_files)

    for msg in sess['chat_history']:
        with st.chat_message(msg["role"]):
            st.markdown(msg["content"])

    question = st.chat_input("Ask a question about tariffs and trade policies")
    if question:
        if not selected_files:
            st.error("Select one or more documents for context before asking a question.")
            st.stop()

        sess['chat_history'].append({
            "role": "user",
            "content": question,
            "timestamp": datetime.utcnow().isoformat(),
        })

        if len(selected_files) == 1:
            context = sess['processed_files_tariff'][selected_files[0]]['context']
        else:
            context = "\n\n".join([
                f"=== Document: {fname} ===\n{sess['processed_files_tariff'][fname]['context']}"
                for fname in selected_files
            ])

        try:
            answer, q_type = st.session_state.chat_agent.generate_answer(question, context)
            sess['chat_history'].append({
                "role": "assistant",
                "content": answer,
                "timestamp": datetime.utcnow().isoformat(),
                "analysis_type": q_type
            })
            st.chat_message("assistant").markdown(answer)
        except Exception as e:
            st.error(f"Error during chat: {str(e)}")
            sess['chat_history'].append({
                "role": "assistant",
                "content": f"Error: {str(e)}",
                "timestamp": datetime.utcnow().isoformat(),
            })

    if st.button("🔄 Reset Session", type="secondary"):
        preserved_keys = [
            "client", "agent_overall", "agent_tariff",
            "agent_sales", "agent_supplychain", "chat_agent", "provider"
        ]
        preserved = {k: st.session_state[k] for k in preserved_keys if k in st.session_state}
        st.session_state.clear()
        st.session_state.update(preserved)
        st.session_state.session = {
            'uploaded_files': {},
            'processed_files_overall': {},
            'processed_files_tariff': {},
            'processed_files_sales': {},
            'processed_files_supplychain': {},
            'active_file': None,
            'active_topic': None,
            'topic_details_cache': {},
            'chat_history': [],
            'consolidated_summary_overall': None,
            'consolidated_summary_tariff': None,
            'consolidated_summary_sales': None,
            'consolidated_summary_supplychain': None,
            'last_analysis_mode': None,
        }
        st.experimental_rerun()

if __name__ == "__main__":
    main()

only change for the streamline for consolidate summary