File size: 5,892 Bytes
c3dec5a
 
7f7b708
c3dec5a
 
7f7b708
 
 
 
c3dec5a
 
 
 
 
231a6dd
c3dec5a
 
 
 
 
351a5f1
231a6dd
c3dec5a
 
 
231a6dd
c3dec5a
 
351a5f1
c3dec5a
351a5f1
5cdb4ed
 
7f7b708
aed3915
b5c0220
231a6dd
e799cfb
351a5f1
c3dec5a
 
7f7b708
231a6dd
7f7b708
231a6dd
7f7b708
231a6dd
7f7b708
351a5f1
aed3915
7f7b708
 
231a6dd
7f7b708
351a5f1
231a6dd
c3dec5a
7f7b708
 
c3dec5a
 
 
 
 
 
 
 
 
 
 
231a6dd
7f7b708
231a6dd
c3dec5a
231a6dd
 
c3dec5a
231a6dd
c3dec5a
 
 
aed3915
c3dec5a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231a6dd
c3dec5a
aed3915
231a6dd
 
 
 
aed3915
 
 
 
 
c3dec5a
 
 
231a6dd
c3dec5a
 
231a6dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351a5f1
 
 
 
 
231a6dd
351a5f1
 
 
231a6dd
aed3915
231a6dd
 
 
 
 
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
154
import os
import sqlite3
import smtplib
import streamlit as st
from typing import List, TypedDict
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# LangChain & Graph Imports
from langchain_openai import ChatOpenAI
from langchain_community.tools.tavily_search import TavilySearchResults
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.sqlite import SqliteSaver

# --- 1. PAGE SETUP ---
st.set_page_config(page_title="Strategic Intel-Agent", layout="wide", page_icon="πŸ›‘οΈ")

st.markdown("""
    <style>
    .main { background-color: #f8f9fa; }
    .stButton>button { width: 100%; border-radius: 8px; height: 3em; font-weight: bold; transition: 0.3s; }
    .report-container { background-color: white; padding: 25px; border-radius: 12px; border: 1px solid #dee2e6; }
    </style>
    """, unsafe_allow_html=True)

# --- 2. SIDEBAR ---
with st.sidebar:
    st.image("https://cdn-icons-png.flaticon.com/512/2103/2103633.png", width=80)
    st.title("Settings & Tools")
    
    with st.expander("πŸ”‘ API Credentials", expanded=True):
        openai_key = os.getenv("OPENAI_API_KEY")
        tavily_key = os.getenv("TAVILY_API_KEY")
        
    with st.expander("πŸ“§ Email Settings (SMTP)", expanded=True):
        sender_email = st.text_input("Your Gmail Address", value="")
        app_password = st.text_input("App Password", type="password")

    thread_id = st.text_input("Session ID", value="global_user_1")
    os.environ["OPENAI_API_KEY"] = openai_key
    os.environ["TAVILY_API_KEY"] = tavily_key

# --- 3. EMAIL TOOL ---
def send_email_smtp(content, recipient, sender, password):
    if not all([content, recipient, sender, password]): return False
    msg = MIMEMultipart()
    msg['From'], msg['To'], msg['Subject'] = sender, recipient, "Market Intelligence Report"
    msg.attach(MIMEText(content, 'plain'))
    try:
        server = smtplib.SMTP('smtp.gmail.com', 587, timeout=10)
        server.starttls()
        server.login(sender, password)
        server.sendmail(sender, recipient, msg.as_string())
        server.quit()
        return True
    except Exception: return False

# --- 4. AGENT LOGIC ---
DB_PATH = "agent_memory.db"
class AgentState(TypedDict):
    company_name: str
    research_data: List[dict]
    iteration: int
    report: str

@st.cache_resource
def initialize_agent():
    llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

    def researcher(state: AgentState):
        # Initializing inside node to prevent the Tavily crash you saw earlier
        search_tool = TavilySearchResults(max_results=3)
        iter_count = state.get("iteration", 0)
        query = f"Financial SWOT and strategy for {state['company_name']} 2025"
        if iter_count > 0:
            query = f"Deep-dive business analysis for {state['company_name']} 2025"
        results = search_tool.invoke({"query": query})
        return {"research_data": results, "iteration": iter_count + 1}

    def writer(state: AgentState):
        context = "\n".join([f"Source: {r['url']}\nContent: {r['content']}" for r in state['research_data']])
        prompt = f"Write a professional SWOT for {state['company_name']}.\n\nDATA:\n{context}"
        response = llm.invoke(prompt)
        return {"report": response.content}

    builder = StateGraph(AgentState)
    builder.add_node("researcher", researcher)
    builder.add_node("writer", writer)
    builder.add_edge(START, "researcher")
    builder.add_edge("researcher", "writer")
    builder.add_edge("writer", END)
    
    conn = sqlite3.connect(DB_PATH, check_same_thread=False)
    return builder.compile(checkpointer=SqliteSaver(conn), interrupt_before=["writer"])

agent_app = initialize_agent()
config = {"configurable": {"thread_id": thread_id}}

# --- 5. MAIN GUI ---
st.title("πŸ›‘οΈ Strategic Intel-Agent")

if "report_output" not in st.session_state:
    st.session_state.report_output = None

# Input Section
company = st.text_input("Target Company Name", placeholder="e.g. NVIDIA")
if st.button("πŸ” Start Research"):
    st.session_state.report_output = None
    agent_app.invoke({"company_name": company, "iteration": 0}, config)
    st.rerun()

current_state = agent_app.get_state(config)

# --- SHOW RESEARCH AND THE TWO BUTTONS ---
if current_state.values.get("research_data"):
    st.divider()
    st.subheader("πŸ“‹ Intelligence Briefing")
    
    with st.expander("πŸ“‚ View Raw Research Data", expanded=True):
        for item in current_state.values["research_data"]:
            st.markdown(f"**Source:** {item['url']}")
            st.caption(item['content'])
            st.divider()

    # THE TWO OPTIONS YOU REQUESTED
    c1, c2 = st.columns(2)
    with c1:
        if st.button("βœ… Approve & Generate SWOT", type="primary"):
            final_res = agent_app.invoke(None, config)
            st.session_state.report_output = final_res.get("report")
            st.rerun()
    with c2:
        if st.button("πŸ”„ Data Insufficient: Search Again"):
            agent_app.update_state(config, {"research_data": []}, as_node="researcher")
            agent_app.invoke(None, config)
            st.rerun()

# --- 6. REPORT & EMAIL CENTER ---
if st.session_state.report_output:
    st.markdown("---")
    res_col, tool_col = st.columns([2, 1])

    with res_col:
        st.markdown("### 🏁 Final SWOT Report")
        st.markdown(f'<div class="report-container">{st.session_state.report_output}</div>', unsafe_allow_html=True)

    with tool_col:
        st.markdown("### πŸš€ Email Delivery")
        email_target = st.text_input("Recipient Email")
        if st.button("πŸ“§ Send via Email"):
            if send_email_smtp(st.session_state.report_output, email_target, sender_email, app_password):
                st.success("Sent Successfully!")
            else:
                st.error("Check SMTP settings & App Password.")