Merge pull request #211 from Madhuvod/multippp

updated product launch intelligence agent
This commit is contained in:
Shubham Saboo 2025-06-02 14:49:37 -05:00 committed by GitHub
commit 5451ecf8d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -7,40 +7,32 @@ from datetime import datetime
from textwrap import dedent from textwrap import dedent
import os import os
# ---------------- Page Config & Styles ---------------- # ---------------- Page Config ----------------
st.set_page_config(page_title="Product Intelligence Agent", page_icon="🚀", layout="wide") st.set_page_config(
page_title="Product Intelligence Agent",
st.markdown( page_icon="🚀",
""" layout="wide",
<style> initial_sidebar_state="expanded"
/* Custom CSS for a sleek look */
.stButton>button {
border-radius: 5px;
height: 3em;
font-weight: 600;
}
.analysis-box {
padding: 1rem;
border-radius: 0.5rem;
background-color: #f9f9f9;
border: 1px solid #e1e1e1;
}
div[data-testid="stExpander"] div[role="button"] p {
font-size: 1.05rem;
font-weight: 600;
}
</style>
""",
unsafe_allow_html=True,
) )
# ---------------- Environment & Agent ---------------- # ---------------- Environment & Agent ----------------
load_dotenv() load_dotenv()
# Add API key inputs in sidebar # Add API key inputs in sidebar
with st.sidebar.expander("🔑 API Keys", expanded=True): st.sidebar.header("🔑 API Configuration")
openai_key = st.text_input("OpenAI API Key", type="password", value=os.getenv("OPENAI_API_KEY", "")) with st.sidebar.container():
firecrawl_key = st.text_input("Firecrawl API Key", type="password", value=os.getenv("FIRECRAWL_API_KEY", "")) openai_key = st.text_input(
"OpenAI API Key",
type="password",
value=os.getenv("OPENAI_API_KEY", ""),
help="Required for AI agent functionality"
)
firecrawl_key = st.text_input(
"Firecrawl API Key",
type="password",
value=os.getenv("FIRECRAWL_API_KEY", ""),
help="Required for web search and crawling"
)
# Set environment variables # Set environment variables
if openai_key: if openai_key:
@ -48,8 +40,9 @@ if openai_key:
if firecrawl_key: if firecrawl_key:
os.environ["FIRECRAWL_API_KEY"] = firecrawl_key os.environ["FIRECRAWL_API_KEY"] = firecrawl_key
# Initialize agent only if both keys are provided # Initialize agents only if both keys are provided
if openai_key and firecrawl_key: if openai_key and firecrawl_key:
# Agent 1: Competitor Launch Analyst
launch_analyst = Agent( launch_analyst = Agent(
name="Product Launch Analyst", name="Product Launch Analyst",
description=dedent(""" description=dedent("""
@ -60,9 +53,55 @@ if openai_key and firecrawl_key:
Where execution fell short (weaknesses) Where execution fell short (weaknesses)
Actionable learnings competitors can leverage Actionable learnings competitors can leverage
Always cite observable signals (messaging, pricing actions, channel mix, timing, engagement metrics). Maintain a crisp, executive tone and focus on strategic value. Always cite observable signals (messaging, pricing actions, channel mix, timing, engagement metrics). Maintain a crisp, executive tone and focus on strategic value.
IMPORTANT: Conclude your report with a 'Sources:' section, listing all URLs of websites you crawled or searched for this analysis.
"""), """),
model=OpenAIChat(id="gpt-4o"), model=OpenAIChat(id="gpt-4o"),
tools=[FirecrawlTools(search=True, crawl=True, limit=8, poll_interval=10)], tools=[FirecrawlTools(search=True, crawl=True, poll_interval=10)],
show_tool_calls=True,
markdown=True,
exponential_backoff=True,
delay_between_retries=2,
)
# Agent 2: Market Sentiment Specialist
sentiment_analyst = Agent(
name="Market Sentiment Specialist",
description=dedent("""
You are a market research expert specializing in sentiment analysis and consumer perception tracking.
Your expertise includes:
Analyzing social media sentiment and customer feedback
Identifying positive and negative sentiment drivers
Tracking brand perception trends across platforms
Monitoring customer satisfaction and review patterns
Providing actionable insights on market reception
Focus on extracting sentiment signals from social platforms, review sites, forums, and customer feedback channels.
IMPORTANT: Conclude your report with a 'Sources:' section, listing all URLs of websites you crawled or searched for this analysis.
"""),
model=OpenAIChat(id="gpt-4o"),
tools=[FirecrawlTools(search=True, crawl=True, poll_interval=10)],
show_tool_calls=True,
markdown=True,
exponential_backoff=True,
delay_between_retries=2,
)
# Agent 3: Launch Metrics Specialist
metrics_analyst = Agent(
name="Launch Metrics Specialist",
description=dedent("""
You are a product launch performance analyst who specializes in tracking and analyzing launch KPIs.
Your focus areas include:
User adoption and engagement metrics
Revenue and business performance indicators
Market penetration and growth rates
Press coverage and media attention analysis
Social media traction and viral coefficient tracking
Competitive market share analysis
Always provide quantitative insights with context and benchmark against industry standards when possible.
IMPORTANT: Conclude your report with a 'Sources:' section, listing all URLs of websites you crawled or searched for this analysis.
"""),
model=OpenAIChat(id="gpt-4o"),
tools=[FirecrawlTools(search=True, crawl=True, poll_interval=10)],
show_tool_calls=True, show_tool_calls=True,
markdown=True, markdown=True,
exponential_backoff=True, exponential_backoff=True,
@ -70,6 +109,8 @@ if openai_key and firecrawl_key:
) )
else: else:
launch_analyst = None launch_analyst = None
sentiment_analyst = None
metrics_analyst = None
st.warning("⚠️ Please enter both API keys in the sidebar to use the application.") st.warning("⚠️ Please enter both API keys in the sidebar to use the application.")
# ---------------- Helper to display response ---------------- # ---------------- Helper to display response ----------------
@ -133,7 +174,7 @@ def expand_competitor_report(bullet_text: str, competitor: str) -> str:
# Helper to craft market sentiment report # Helper to craft market sentiment report
def expand_sentiment_report(bullet_text: str, product: str) -> str: def expand_sentiment_report(bullet_text: str, product: str) -> str:
if not launch_analyst: if not sentiment_analyst:
st.error("⚠️ Please enter both API keys in the sidebar first.") st.error("⚠️ Please enter both API keys in the sidebar first.")
return "" return ""
@ -147,12 +188,12 @@ def expand_sentiment_report(bullet_text: str, product: str) -> str:
f"Provide a short paragraph (≤120 words) summarising the overall sentiment balance and key drivers.\n\n" f"Provide a short paragraph (≤120 words) summarising the overall sentiment balance and key drivers.\n\n"
f"Tagged Bullets:\n{bullet_text}" f"Tagged Bullets:\n{bullet_text}"
) )
resp = launch_analyst.run(prompt) resp = sentiment_analyst.run(prompt)
return resp.content if hasattr(resp, "content") else str(resp) return resp.content if hasattr(resp, "content") else str(resp)
# Helper to craft launch metrics report # Helper to craft launch metrics report
def expand_metrics_report(bullet_text: str, launch: str) -> str: def expand_metrics_report(bullet_text: str, launch: str) -> str:
if not launch_analyst: if not metrics_analyst:
st.error("⚠️ Please enter both API keys in the sidebar first.") st.error("⚠️ Please enter both API keys in the sidebar first.")
return "" return ""
@ -168,137 +209,296 @@ def expand_metrics_report(bullet_text: str, launch: str) -> str:
f"Brief paragraph (≤120 words) highlighting what the metrics imply about launch success and next steps.\n\n" f"Brief paragraph (≤120 words) highlighting what the metrics imply about launch success and next steps.\n\n"
f"KPI Bullets:\n{bullet_text}" f"KPI Bullets:\n{bullet_text}"
) )
resp = launch_analyst.run(prompt) resp = metrics_analyst.run(prompt)
return resp.content if hasattr(resp, "content") else str(resp) return resp.content if hasattr(resp, "content") else str(resp)
# ---------------- UI ---------------- # ---------------- UI ----------------
st.title("🚀 Product Launch Intelligence Agent") st.title("🚀 Product Launch Intelligence Agent")
st.caption("AI Agent powered insights for GTM, Product Marketing & Growth Teams") st.markdown("*AI-powered insights for GTM, Product Marketing & Growth Teams*")
st.divider()
# Company input section
st.subheader("🏢 Company Analysis")
with st.container():
col1, col2 = st.columns([3, 1])
with col1:
company_name = st.text_input(
label="Company Name",
placeholder="Enter company name (e.g., OpenAI, Tesla, Spotify)",
help="This company will be analyzed by all three specialized agents",
label_visibility="collapsed"
)
with col2:
if company_name:
st.success(f"✓ Ready to analyze **{company_name}**")
st.divider()
# Create tabs for analysis types # Create tabs for analysis types
analysis_tabs = st.tabs(["Competitor Analysis", "Market Sentiment", "Launch Metrics"]) analysis_tabs = st.tabs([
"🔍 Competitor Analysis",
"💬 Market Sentiment",
"📈 Launch Metrics"
])
# Persistent storage for latest response # Persistent storage for latest response
if "analysis_response" not in st.session_state: if "analysis_response" not in st.session_state:
st.session_state.analysis_response = None st.session_state.analysis_response = None
st.session_state.analysis_meta = {} st.session_state.analysis_meta = {}
# Store separate responses for each agent
if "competitor_response" not in st.session_state:
st.session_state.competitor_response = None
if "sentiment_response" not in st.session_state:
st.session_state.sentiment_response = None
if "metrics_response" not in st.session_state:
st.session_state.metrics_response = None
# -------- Competitor Analysis Tab -------- # -------- Competitor Analysis Tab --------
with analysis_tabs[0]: with analysis_tabs[0]:
st.subheader("🔍 Competitor Launch Analysis") with st.container():
competitor_name = st.text_input("Competitor name", key="competitor_input") st.markdown("### 🔍 Competitor Launch Analysis")
cols = st.columns([2, 1]) with st.expander(" About this Agent", expanded=False):
with cols[0]: st.markdown("""
if st.button("Analyze", key="competitor_btn") and competitor_name: **Product Launch Analyst** - Strategic GTM Expert
if not launch_analyst:
st.error("⚠️ Please enter both API keys in the sidebar first.")
else:
with st.spinner("Gathering competitive insights..."):
try:
bullets = launch_analyst.run(
f"Generate up to 16 evidence-based insight bullets about {competitor_name}'s most recent product launches.\n"
f"Format requirements:\n"
f"• Start every bullet with exactly one tag: Positioning | Strength | Weakness | Learning\n"
f"• Follow the tag with a concise statement (max 30 words) referencing concrete observations: messaging, differentiation, pricing, channel selection, timing, engagement metrics, or customer feedback."
)
long_text = expand_competitor_report(
bullets.content if hasattr(bullets, "content") else str(bullets),
competitor_name
)
st.session_state.analysis_response = long_text
st.session_state.analysis_meta = {
"type": "Competitor Analysis",
"query": competitor_name,
"timestamp": datetime.utcnow().isoformat()
}
st.success("✅ Analysis ready")
except Exception as e:
st.error(f"❌ Error: {e}")
if st.session_state.analysis_response and st.session_state.analysis_meta.get("type") == "Competitor Analysis": Specializes in:
st.markdown("### 📊 Results") - Competitive positioning analysis
st.markdown(st.session_state.analysis_response) - Launch strategy evaluation
- Strengths & weaknesses identification
- Strategic recommendations
""")
if company_name:
col1, col2 = st.columns([2, 1])
with col1:
analyze_btn = st.button(
"🚀 Analyze Competitor Strategy",
key="competitor_btn",
type="primary",
use_container_width=True
)
with col2:
if st.session_state.competitor_response:
st.success("✅ Analysis Complete")
else:
st.info("⏳ Ready to analyze")
if analyze_btn:
if not launch_analyst:
st.error("⚠️ Please enter both API keys in the sidebar first.")
else:
with st.spinner("🔍 Launch Analyst gathering competitive intelligence..."):
try:
bullets = launch_analyst.run(
f"Generate up to 16 evidence-based insight bullets about {company_name}'s most recent product launches.\n"
f"Format requirements:\n"
f"• Start every bullet with exactly one tag: Positioning | Strength | Weakness | Learning\n"
f"• Follow the tag with a concise statement (max 30 words) referencing concrete observations: messaging, differentiation, pricing, channel selection, timing, engagement metrics, or customer feedback."
)
long_text = expand_competitor_report(
bullets.content if hasattr(bullets, "content") else str(bullets),
company_name
)
st.session_state.competitor_response = long_text
st.success("✅ Competitor analysis ready")
st.rerun()
except Exception as e:
st.error(f"❌ Error: {e}")
# Display results
if st.session_state.competitor_response:
st.divider()
with st.container():
st.markdown("### 📊 Analysis Results")
st.markdown(st.session_state.competitor_response)
else:
st.info("👆 Please enter a company name above to start the analysis")
# -------- Market Sentiment Tab -------- # -------- Market Sentiment Tab --------
with analysis_tabs[1]: with analysis_tabs[1]:
st.subheader("💬 Market Sentiment Analysis") with st.container():
product_name = st.text_input("Product name", key="sentiment_input") st.markdown("### 💬 Market Sentiment Analysis")
cols = st.columns([2, 1]) with st.expander(" About this Agent", expanded=False):
with cols[0]: st.markdown("""
if st.button("Analyze", key="sentiment_btn") and product_name: **Market Sentiment Specialist** - Consumer Perception Expert
if not launch_analyst:
st.error("⚠️ Please enter both API keys in the sidebar first.")
else:
with st.spinner("Collecting market sentiment..."):
try:
bullets = launch_analyst.run(
f"Summarize market sentiment for {product_name} in <=10 bullets. "
f"Cover top positive & negative themes with source mentions (G2, Reddit, Twitter)."
)
long_text = expand_sentiment_report(
bullets.content if hasattr(bullets, "content") else str(bullets),
product_name
)
st.session_state.analysis_response = long_text
st.session_state.analysis_meta = {
"type": "Market Sentiment",
"query": product_name,
"timestamp": datetime.utcnow().isoformat()
}
st.success("✅ Sentiment analysis ready")
except Exception as e:
st.error(f"❌ Error: {e}")
if st.session_state.analysis_response and st.session_state.analysis_meta.get("type") == "Market Sentiment": Specializes in:
st.markdown("### 📈 Sentiment Insights") - Social media sentiment tracking
st.markdown(st.session_state.analysis_response) - Customer feedback analysis
- Brand perception monitoring
- Review pattern identification
""")
if company_name:
col1, col2 = st.columns([2, 1])
with col1:
sentiment_btn = st.button(
"📊 Analyze Market Sentiment",
key="sentiment_btn",
type="primary",
use_container_width=True
)
with col2:
if st.session_state.sentiment_response:
st.success("✅ Analysis Complete")
else:
st.info("⏳ Ready to analyze")
if sentiment_btn:
if not sentiment_analyst:
st.error("⚠️ Please enter both API keys in the sidebar first.")
else:
with st.spinner("💬 Sentiment Specialist analyzing market perception..."):
try:
bullets = sentiment_analyst.run(
f"Summarize market sentiment for {company_name} in <=10 bullets. "
f"Cover top positive & negative themes with source mentions (G2, Reddit, Twitter, customer reviews)."
)
long_text = expand_sentiment_report(
bullets.content if hasattr(bullets, "content") else str(bullets),
company_name
)
st.session_state.sentiment_response = long_text
st.success("✅ Sentiment analysis ready")
st.rerun()
except Exception as e:
st.error(f"❌ Error: {e}")
# Display results
if st.session_state.sentiment_response:
st.divider()
with st.container():
st.markdown("### 📈 Analysis Results")
st.markdown(st.session_state.sentiment_response)
else:
st.info("👆 Please enter a company name above to start the analysis")
# -------- Launch Metrics Tab -------- # -------- Launch Metrics Tab --------
with analysis_tabs[2]: with analysis_tabs[2]:
st.subheader("📈 Launch Performance Metrics") with st.container():
product_launch = st.text_input("Product name / Launch campaign", key="metrics_input") st.markdown("### 📈 Launch Performance Metrics")
cols = st.columns([2, 1]) with st.expander(" About this Agent", expanded=False):
with cols[0]: st.markdown("""
if st.button("Analyze", key="metrics_btn") and product_launch: **Launch Metrics Specialist** - Performance Analytics Expert
if not launch_analyst:
st.error("⚠️ Please enter both API keys in the sidebar first.")
else:
with st.spinner("Fetching launch performance data..."):
try:
bullets = launch_analyst.run(
f"List (max 10 bullets) the most important publicly available KPIs & qualitative signals for {product_launch}. "
f"Include engagement stats, press coverage and social traction if available."
)
long_text = expand_metrics_report(
bullets.content if hasattr(bullets, "content") else str(bullets),
product_launch
)
st.session_state.analysis_response = long_text
st.session_state.analysis_meta = {
"type": "Launch Metrics",
"query": product_launch,
"timestamp": datetime.utcnow().isoformat()
}
st.success("✅ Metrics analysis ready")
except Exception as e:
st.error(f"❌ Error: {e}")
if st.session_state.analysis_response and st.session_state.analysis_meta.get("type") == "Launch Metrics": Specializes in:
st.markdown("### 📊 Metric Highlights") - User adoption metrics tracking
st.markdown(st.session_state.analysis_response) - Revenue performance analysis
- Market penetration evaluation
- Press coverage monitoring
""")
if company_name:
col1, col2 = st.columns([2, 1])
with col1:
metrics_btn = st.button(
"📊 Analyze Launch Metrics",
key="metrics_btn",
type="primary",
use_container_width=True
)
with col2:
if st.session_state.metrics_response:
st.success("✅ Analysis Complete")
else:
st.info("⏳ Ready to analyze")
if metrics_btn:
if not metrics_analyst:
st.error("⚠️ Please enter both API keys in the sidebar first.")
else:
with st.spinner("📈 Metrics Specialist analyzing launch performance..."):
try:
bullets = metrics_analyst.run(
f"List (max 10 bullets) the most important publicly available KPIs & qualitative signals for {company_name}'s recent product launches. "
f"Include engagement stats, press coverage, adoption metrics, and market traction data if available."
)
long_text = expand_metrics_report(
bullets.content if hasattr(bullets, "content") else str(bullets),
company_name
)
st.session_state.metrics_response = long_text
st.success("✅ Metrics analysis ready")
st.rerun()
except Exception as e:
st.error(f"❌ Error: {e}")
# Display results
if st.session_state.metrics_response:
st.divider()
with st.container():
st.markdown("### 📊 Analysis Results")
st.markdown(st.session_state.metrics_response)
else:
st.info("👆 Please enter a company name above to start the analysis")
# ---------------- Sidebar ---------------- # ---------------- Sidebar ----------------
st.sidebar.header(" About") # Agent status indicators
st.sidebar.markdown( with st.sidebar.container():
""" st.markdown("### 🤖 System Status")
**Product Launch Intelligence Agent** helps GTM teams quickly: if openai_key and firecrawl_key:
- Benchmark competitor launches st.success("✅ All agents ready")
- Monitor market sentiment pre/post-launch else:
- Track launch performance signals st.error("❌ API keys required")
Built with **Agno** & **Firecrawl**. st.sidebar.divider()
"""
) # Multi-agent system info
with st.sidebar.container():
st.markdown("### 🎯 Specialized Agents")
agents_info = [
("🔍", "Product Launch Analyst", "Strategic GTM expert"),
("💬", "Market Sentiment Specialist", "Consumer perception expert"),
("📈", "Launch Metrics Specialist", "Performance analytics expert")
]
for icon, name, desc in agents_info:
with st.container():
st.markdown(f"**{icon} {name}**")
st.caption(desc)
st.sidebar.divider()
# Analysis status
if company_name:
with st.sidebar.container():
st.markdown("### 📊 Analysis Status")
st.markdown(f"**Company:** {company_name}")
status_items = [
("🔍", "Competitor Analysis", st.session_state.competitor_response),
("💬", "Sentiment Analysis", st.session_state.sentiment_response),
("📈", "Metrics Analysis", st.session_state.metrics_response)
]
for icon, name, status in status_items:
if status:
st.success(f"{icon} {name}")
else:
st.info(f"{icon} {name}")
st.sidebar.divider()
# Quick actions
with st.sidebar.container():
st.markdown("### ⚡ Quick Actions")
if company_name:
st.markdown("""
**J** - Competitor analysis
**K** - Market sentiment
**L** - Launch metrics
""")
else:
st.info("Enter a company name to enable quick actions")