Kashaf23's picture
Upload 3 files
11480af verified
Raw
History Blame Contribute Delete
38.2 kB
import os
import json
import re
import faiss
import numpy as np
import gradio as gr
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
import tempfile
from dotenv import load_dotenv
from sentence_transformers import SentenceTransformer
from groq import Groq
# ==========================
# OPTIONAL LIBRARIES
# ==========================
try:
from fpdf import FPDF
import kaleido
PDF_AVAILABLE = True
except ImportError:
PDF_AVAILABLE = False
try:
from sklearn.decomposition import PCA
SKLEARN_AVAILABLE = True
except ImportError:
SKLEARN_AVAILABLE = False
def random_projection(embeddings, n_components=3):
np.random.seed(42)
projection = np.random.randn(embeddings.shape[1], n_components)
return np.dot(embeddings, projection)
# ==========================
# LOAD ENV & CONFIG
# ==========================
load_dotenv()
groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))
BRAND_NAME = os.getenv("BRAND_NAME", "IdeaIQ")
LOGO_PATH = os.getenv("LOGO_PATH", "")
# ==========================
# LOAD DATASET
# ==========================
DATA_PATH = "cleaned_market_trends.json" # adjust if needed
with open(DATA_PATH, "r", encoding="utf-8") as f:
documents = json.load(f)
# ==========================
# EMBEDDINGS + FAISS
# ==========================
embedding_model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
texts = [doc["content"] for doc in documents]
embeddings = embedding_model.encode(texts)
embeddings = np.array(embeddings).astype("float32")
faiss.normalize_L2(embeddings)
dimension = embeddings.shape[1]
faiss_index = faiss.IndexFlatIP(dimension)
faiss_index.add(embeddings)
# ==========================
# RETRIEVAL
# ==========================
def retrieve_context(user_idea, top_k=4):
query_embedding = embedding_model.encode([user_idea])
query_embedding = np.array(query_embedding).astype("float32")
faiss.normalize_L2(query_embedding)
scores, indices = faiss_index.search(query_embedding, top_k)
retrieved_docs = []
similarity_scores = []
for score, idx in zip(scores[0], indices[0]):
retrieved_docs.append(documents[idx])
similarity_scores.append(float(score))
avg_similarity = round(float(np.mean(similarity_scores)), 3)
return retrieved_docs, avg_similarity
# ==========================
# JSON CLEANER
# ==========================
def extract_json(text):
text = re.sub(r"```json", "", text)
text = re.sub(r"```", "", text)
match = re.search(r"\{.*\}", text, re.DOTALL)
if match:
return json.loads(match.group())
raise ValueError("Invalid JSON response")
# ==========================
# LLM ANALYSIS
# ==========================
SYSTEM_PROMPT = """
You are an AI Product Strategy Analyst.
Return ONLY valid JSON.
Do not wrap in markdown.
Strictly follow provided structure.
"""
def analyze_idea(user_idea):
retrieved_docs, avg_similarity = retrieve_context(user_idea)
context_text = "\n\n".join(
[f"{doc['domain']} | {doc['year']} | {doc['content']}" for doc in retrieved_docs]
)
user_prompt = f"""
User Idea:
{user_idea}
Market Context:
{context_text}
Return strictly a JSON object with the following structure. The "market_analysis" list should contain one object per retrieved document (exactly {len(retrieved_docs)} items), with estimated numeric scores based on the document content and your market knowledge.
{{
"idea": "{user_idea}",
"scope": "Low | Medium | High",
"feasibility": "Low | Medium | High",
"estimated_timeline": "X months",
"market_trend_summary": "...",
"uniqueness_analysis": "...",
"risks": ["Risk 1", "Risk 2"],
"actionable_steps": ["Step 1", "Step 2"],
"project_score": 0-10,
"market_analysis": [
{{
"domain": "from document",
"year": "from document",
"category": "e.g., AI, Healthcare, Fintech",
"region": "Global | North America | etc.",
"growth_rate": 0-100,
"innovation_score": 0-100,
"market_potential": 0-100,
"competition_level": 0-100
}},
...
]
}}
"""
response = groq_client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_prompt}
],
temperature=0
)
result = extract_json(response.choices[0].message.content)
result.setdefault("scope", "Medium")
result.setdefault("feasibility", "Medium")
result.setdefault("estimated_timeline", "6 months")
result.setdefault("market_trend_summary", "No summary")
result.setdefault("uniqueness_analysis", "No analysis")
result.setdefault("risks", ["Unknown"])
result.setdefault("actionable_steps", ["Unknown"])
result.setdefault("project_score", 5)
result.setdefault("market_analysis", [])
if not result["market_analysis"] and retrieved_docs:
for doc in retrieved_docs:
result["market_analysis"].append({
"domain": doc["domain"],
"year": doc["year"],
"category": "Unknown",
"region": "Global",
"growth_rate": np.random.randint(30, 90),
"innovation_score": np.random.randint(30, 90),
"market_potential": np.random.randint(30, 90),
"competition_level": np.random.randint(30, 90)
})
result["similarity_score"] = avg_similarity
result["retrieved_docs"] = retrieved_docs
return result
# ==========================
# VISUALIZATION FUNCTIONS
# ==========================
def create_gauge(result):
project_score = result["project_score"]
fig = go.Figure(go.Indicator(
mode="gauge+number",
value=project_score,
number={'suffix': "/10", 'font': {'size': 30}},
title={'text': "Project Score", 'font': {'size': 20}},
gauge={
'axis': {'range': [0, 10], 'tickwidth': 1},
'bar': {'color': "darkblue", 'thickness': 0.3},
'bgcolor': "white",
'borderwidth': 2,
'steps': [
{'range': [0, 3], 'color': '#ffcccc'},
{'range': [3, 7], 'color': '#ffffcc'},
{'range': [7, 10], 'color': '#ccffcc'}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 7
}
}
))
fig.update_layout(height=250, margin=dict(l=20, r=20, t=40, b=20),
paper_bgcolor='rgba(0,0,0,0)')
return fig
def create_radial_graph(result, visible_nodes=8):
idea = result["idea"]
project_score = result["project_score"]
feasibility = result["feasibility"]
similarity = result["similarity_score"]
scope = result["scope"]
timeline = result["estimated_timeline"]
score_color = f"rgb({255-int(project_score*25)}, {int(project_score*25)}, 0)"
feasibility_color = {"High": "#00cc96", "Medium": "#ffa15e", "Low": "#ef553b"}.get(feasibility, "#636efa")
nodes = [
idea,
f"Scope: {scope}",
f"Feasibility: {feasibility}",
f"Timeline: {timeline}",
"Risks",
"Actionable Steps",
"Uniqueness",
f"Similarity: {similarity}"
]
angle = np.linspace(0, 2*np.pi, len(nodes))
radius = 2.5
x = [0] + list(radius * np.cos(angle[1:]))
y = [0] + list(radius * np.sin(angle[1:]))
colors = [
score_color,
"#1f77b4",
feasibility_color,
"#ff7f0e",
"#d62728",
"#9467bd",
"#17becf",
"#bcbd22"
]
sizes = [60, 35, 35, 35, 40, 40, 35, 35]
hover_texts = [
f"<b>Idea</b><br>{idea}<br><b>Score: {project_score}/10</b>",
f"<b>Scope</b><br>{scope}",
f"<b>Feasibility</b><br>{feasibility}",
f"<b>Timeline</b><br>{timeline}",
f"<b>Risks</b><br>" + "<br>".join(result["risks"]),
f"<b>Actionable Steps</b><br>" + "<br>".join(result["actionable_steps"]),
f"<b>Uniqueness</b><br>{result['uniqueness_analysis']}",
f"<b>Similarity Score</b><br>{similarity}"
]
visible = min(visible_nodes, len(nodes))
visible_x = x[:visible]
visible_y = y[:visible]
visible_text = nodes[:visible]
visible_hover = hover_texts[:visible]
visible_colors = colors[:visible]
visible_sizes = sizes[:visible]
edge_x, edge_y = [], []
for i in range(1, visible):
edge_x += [x[0], x[i], None]
edge_y += [y[0], y[i], None]
fig = go.Figure()
fig.add_trace(go.Scatter(
x=edge_x, y=edge_y,
mode='lines',
line=dict(width=1.5, color='#888'),
hoverinfo='none'
))
fig.add_trace(go.Scatter(
x=visible_x, y=visible_y,
mode='markers+text',
text=visible_text,
textposition='bottom center',
hovertext=visible_hover,
hoverinfo='text',
marker=dict(
size=visible_sizes,
color=visible_colors,
line=dict(width=2, color='white')
),
textfont=dict(size=12, color='black')
))
fig.update_layout(
title=dict(text="🚀 Strategic Opportunity Map", font=dict(size=20, family='Arial Black')),
showlegend=False,
xaxis=dict(visible=False),
yaxis=dict(visible=False),
height=550,
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
margin=dict(l=20, r=20, t=80, b=20)
)
return fig
def create_3d_market(result):
retrieved_docs = result.get("retrieved_docs", [])
idea = result["idea"]
if not retrieved_docs:
return go.Figure()
doc_texts = [doc["content"] for doc in retrieved_docs]
all_texts = [idea] + doc_texts
all_embeddings = embedding_model.encode(all_texts)
if SKLEARN_AVAILABLE:
pca = PCA(n_components=3)
coords = pca.fit_transform(all_embeddings)
else:
coords = random_projection(all_embeddings, 3)
df = pd.DataFrame({
'x': coords[:,0],
'y': coords[:,1],
'z': coords[:,2],
'label': ['Your Idea'] + [f"{doc['domain']} ({doc['year']})" for doc in retrieved_docs],
'size': [20] + [15]*len(retrieved_docs),
'color': ['red'] + ['blue']*len(retrieved_docs),
'hover': [f"<b>Your Idea</b><br>{idea}"] +
[f"<b>{doc['domain']} ({doc['year']})</b><br>{doc['content'][:100]}..." for doc in retrieved_docs]
})
fig = px.scatter_3d(df, x='x', y='y', z='z', text='label', size='size', color='color',
hover_data={'hover': True, 'x': False, 'y': False, 'z': False, 'color': False, 'size': False},
title="🧠 Market Landscape (3D Similarity Space)")
fig.update_traces(marker=dict(line=dict(width=2, color='white')),
textposition='top center')
fig.update_layout(height=550, showlegend=False)
return fig
def create_kpi_panel(df):
fig = go.Figure()
metrics = {
"Avg Growth": df["growth_rate"].mean(),
"Innovation Strength": df["innovation_score"].mean(),
"Market Potential": df["market_potential"].mean(),
"Strategic Advantage": (df["innovation_score"] + df["market_potential"] - df["competition_level"]).mean()
}
fig.add_trace(go.Indicator(
mode="number",
value=round(metrics["Avg Growth"], 2),
title={"text": "Avg Growth"},
domain={'x': [0, 0.45], 'y': [0.55, 1]},
number={"font": {"size": 36}}
))
fig.add_trace(go.Indicator(
mode="number",
value=round(metrics["Innovation Strength"], 2),
title={"text": "Innovation Strength"},
domain={'x': [0.55, 1], 'y': [0.55, 1]},
number={"font": {"size": 36}}
))
fig.add_trace(go.Indicator(
mode="number",
value=round(metrics["Market Potential"], 2),
title={"text": "Market Potential"},
domain={'x': [0, 0.45], 'y': [0, 0.45]},
number={"font": {"size": 36}}
))
fig.add_trace(go.Indicator(
mode="number",
value=round(metrics["Strategic Advantage"], 2),
title={"text": "Strategic Advantage"},
domain={'x': [0.55, 1], 'y': [0, 0.45]},
number={"font": {"size": 36}}
))
fig.update_layout(
template="plotly_dark",
height=320,
margin=dict(t=20, b=20, l=20, r=20)
)
return fig
def create_positioning_matrix(df):
fig = px.scatter(
df,
x="competition_level",
y="innovation_score",
size="market_potential",
color="growth_rate",
hover_data=["domain","category","region","year"],
title="Strategic Positioning Matrix",
size_max=50,
color_continuous_scale="Turbo"
)
fig.update_traces(
marker=dict(line=dict(width=1, color='white')),
hovertemplate="<b>%{customdata[0]}</b><br>Category: %{customdata[1]}<br>Region: %{customdata[2]}<br>Year: %{customdata[3]}<br>Growth: %{color:.1f}<extra></extra>"
)
fig.update_layout(
template="plotly_dark",
height=400,
hovermode="closest",
transition={"duration": 500}
)
return fig
def create_growth_by_year(df, selected_year):
if "year" not in df.columns or df.empty:
fig = go.Figure()
fig.add_annotation(text="No data available", showarrow=False)
fig.update_layout(template="plotly_dark", height=400)
return fig
# Convert year column to string to match slider value
df = df.copy()
df['year'] = df['year'].astype(str)
df_filtered = df[df["year"] == selected_year].copy()
if df_filtered.empty:
fig = go.Figure()
fig.add_annotation(text=f"No data for year {selected_year}", showarrow=False)
fig.update_layout(template="plotly_dark", height=400)
return fig
fig = px.bar(
df_filtered,
x="domain",
y="growth_rate",
color="growth_rate",
title=f"Domain Growth - {selected_year}",
color_continuous_scale="Viridis",
range_y=[0, 100],
hover_data=["category", "region"]
)
fig.update_traces(
hovertemplate="<b>%{x}</b><br>Growth: %{y:.1f}<br>Category: %{customdata[0]}<br>Region: %{customdata[1]}<extra></extra>"
)
fig.update_layout(
template="plotly_dark",
height=400,
xaxis_title="Domain",
yaxis_title="Growth Rate (%)"
)
return fig
def create_heatmap(df):
if "year" not in df.columns:
return go.Figure()
pivot = df.pivot_table(values="market_potential", index="domain", columns="year")
fig = px.imshow(
pivot,
color_continuous_scale="Magma",
title="Market Potential Heatmap",
aspect="auto",
labels=dict(x="Year", y="Domain", color="Potential")
)
fig.update_layout(template="plotly_dark", height=400, transition={"duration": 500})
fig.update_traces(hovertemplate="Year: %{x}<br>Domain: %{y}<br>Potential: %{z:.1f}<extra></extra>")
return fig
def create_opportunity_radar(df):
if df.empty:
return go.Figure()
df["advantage"] = df["innovation_score"] + df["market_potential"] - df["competition_level"]
top = df.sort_values("advantage", ascending=False).iloc[0]
categories = ["Growth", "Innovation", "Market", "Competitive Advantage"]
values = [
top["growth_rate"],
top["innovation_score"],
top["market_potential"],
100 - top["competition_level"]
]
fig = go.Figure()
fig.add_trace(go.Scatterpolar(
r=values,
theta=categories,
fill="toself",
marker=dict(color='rgba(102, 126, 234, 0.8)'),
hovertemplate="%{theta}: %{r:.1f}<extra></extra>"
))
fig.update_layout(
template="plotly_dark",
polar=dict(radialaxis=dict(range=[0,100], visible=True)),
title=f"Top Opportunity: {top['domain']}",
height=400,
transition={"duration": 500}
)
return fig
# ==========================
# PDF GENERATION (FIXED)
# ==========================
REPORTS_DIR = "reports"
os.makedirs(REPORTS_DIR, exist_ok=True)
def generate_pdf(result, radial_fig):
if not PDF_AVAILABLE:
error_path = os.path.join(REPORTS_DIR, "pdf_error.txt")
with open(error_path, "w") as f:
f.write("PDF generation is not available because required libraries (fpdf2, kaleido) are missing.\nPlease install them.")
return error_path
try:
# Test if kaleido can export (will fail on HF if chromium missing)
try:
_ = radial_fig.to_image(format="png")
except Exception as e:
error_path = os.path.join(REPORTS_DIR, "export_error.txt")
with open(error_path, "w") as f:
f.write(f"Failed to export plot image: {str(e)}\nThis may be due to missing Chromium.")
return error_path
pdf = FPDF()
pdf.add_page()
if LOGO_PATH and os.path.exists(LOGO_PATH):
pdf.image(LOGO_PATH, x=10, y=8, w=30)
pdf.set_font("Arial", 'B', 16)
pdf.cell(0, 10, BRAND_NAME, ln=1, align='C')
pdf.ln(10)
pdf.set_font("Arial", 'B', 12)
pdf.cell(0, 10, "Executive Summary", ln=1)
pdf.set_font("Arial", '', 10)
summary = f"""
Project Score: {result['project_score']}/10
Feasibility: {result['feasibility']}
Timeline: {result['estimated_timeline']}
Market Similarity: {result['similarity_score']}
Market Trend Summary:
{result['market_trend_summary']}
Uniqueness Analysis:
{result['uniqueness_analysis']}
Top Risks:
{chr(10).join(['- ' + r for r in result['risks']])}
Actionable Steps:
{chr(10).join(['- ' + s for s in result['actionable_steps']])}
"""
pdf.multi_cell(0, 5, summary)
pdf.ln(5)
# Add radial graph image
img_bytes = radial_fig.to_image(format="png")
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_img:
tmp_img.write(img_bytes)
img_path = tmp_img.name
pdf.image(img_path, w=180)
os.unlink(img_path)
pdf.ln(5)
pdf.set_font("Arial", 'B', 12)
pdf.cell(0, 10, "Structured Report (JSON)", ln=1)
pdf.set_font("Courier", '', 8)
pdf.multi_cell(0, 4, json.dumps(result, indent=2))
# Save PDF with timestamp
pdf_filename = f"report_{pd.Timestamp.now().strftime('%Y%m%d_%H%M%S')}.pdf"
pdf_path = os.path.join(REPORTS_DIR, pdf_filename)
pdf.output(pdf_path)
if os.path.exists(pdf_path) and os.path.getsize(pdf_path) > 0:
return pdf_path
else:
error_path = os.path.join(REPORTS_DIR, "empty_pdf.txt")
with open(error_path, "w") as f:
f.write("PDF file was created but appears empty.")
return error_path
except Exception as e:
print(f"PDF generation error: {e}")
error_path = os.path.join(REPORTS_DIR, "pdf_error.txt")
with open(error_path, "w") as f:
f.write(f"An error occurred during PDF generation: {str(e)}")
return error_path
def prepare_pdf(result, radial_fig):
if not result or not radial_fig:
error_path = os.path.join(REPORTS_DIR, "no_data.txt")
with open(error_path, "w") as f:
f.write("No data available for PDF generation.")
return error_path
return generate_pdf(result, radial_fig)
# ==========================
# UPDATE FUNCTIONS
# ==========================
def update_radial_and_state(result, step):
if not result:
return go.Figure(), go.Figure()
new_fig = create_radial_graph(result, step)
return new_fig, new_fig
def update_live_score(result_state):
if result_state:
new_score = result_state.get("similarity_score", 0.5)
variation = np.random.uniform(-0.05, 0.05)
new_score = round(max(0, min(1, new_score + variation)), 3)
result_state["similarity_score"] = new_score
return f"<span class='live-badge'>Live Similarity: {new_score}</span>"
return "<span class='live-badge'>Live Similarity: N/A</span>"
def increment_step(current_step):
return min(current_step + 1, 8)
def update_growth_by_year(result_state, selected_year):
if not result_state:
return go.Figure()
df = pd.DataFrame(result_state.get("market_analysis", []))
for col in ["growth_rate","innovation_score","market_potential","competition_level"]:
if col in df.columns:
df[col] = pd.to_numeric(df[col], errors="coerce")
df.fillna(0, inplace=True)
return create_growth_by_year(df, selected_year)
# ==========================
# FULL PIPELINE
# ==========================
def full_pipeline(user_input):
try:
result = analyze_idea(user_input)
fig_radial = create_radial_graph(result, visible_nodes=1)
fig_3d = create_3d_market(result)
fig_gauge = create_gauge(result)
df = pd.DataFrame(result["market_analysis"])
for col in ["growth_rate","innovation_score","market_potential","competition_level"]:
if col in df.columns:
df[col] = pd.to_numeric(df[col], errors="coerce")
df.fillna(0, inplace=True)
years = []
if "year" in df.columns:
years = sorted(df["year"].unique())
years = [str(y) for y in years]
if df.empty:
fig_kpi = go.Figure()
fig_matrix = go.Figure()
fig_growth = create_growth_by_year(df, None)
fig_heat = go.Figure()
fig_radar_opp = go.Figure()
else:
fig_kpi = create_kpi_panel(df)
fig_matrix = create_positioning_matrix(df)
initial_year = str(df["year"].iloc[0]) if "year" in df.columns else None
fig_growth = create_growth_by_year(df, initial_year)
fig_heat = create_heatmap(df)
fig_radar_opp = create_opportunity_radar(df)
summary_md = f"""
### 📊 Key Metrics
- **Project Score**: **{result['project_score']}/10**
- **Feasibility**: **{result['feasibility']}**
- **Timeline**: **{result['estimated_timeline']}**
- **Market Similarity**: **{result['similarity_score']}**
### 📈 Market Trend Summary
{result['market_trend_summary']}
### 💡 Uniqueness Analysis
{result['uniqueness_analysis']}
### ⚠️ Top Risks
{chr(10).join(['- ' + r for r in result['risks']])}
### ✅ Actionable Steps
{chr(10).join(['- ' + s for s in result['actionable_steps']])}
"""
confetti_html = ""
if result['project_score'] > 8:
confetti_html = """
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1"></script>
<script>
confetti({particleCount: 100, spread: 70, origin: { y: 0.6 }});
</script>
"""
return (summary_md, json.dumps(result, indent=4),
fig_radial, fig_3d, fig_gauge,
fig_kpi, fig_matrix, fig_growth, fig_heat, fig_radar_opp,
result, fig_radial, confetti_html, years)
except Exception as e:
error_msg = f"❌ Error: {str(e)}"
empty_fig = go.Figure()
return (error_msg, "{}", empty_fig, empty_fig, empty_fig,
empty_fig, empty_fig, empty_fig, empty_fig, empty_fig,
{}, empty_fig, "", [])
# ==========================
# ULTRA‑PREMIUM RESPONSIVE UI
# ==========================
with gr.Blocks(theme=gr.themes.Soft(), css="") as app:
gr.HTML("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: #f8fafc; /* Soft, professional light gray */
min-height: 100vh;
}
.gradio-container {
max-width: 1600px;
margin: 1.5rem auto;
padding: 1.5rem;
background: #ffffff;
border-radius: 2rem;
box-shadow: 0 20px 40px -10px rgba(0,0,0,0.1);
}
/* Animated gradient header */
.premium-header {
background: linear-gradient(-45deg, #1e3c72, #2a5298, #1e3c72, #162b4c);
background-size: 400% 400%;
animation: gradient 15s ease infinite;
padding: 2.5rem 2rem;
border-radius: 1.5rem;
color: white;
text-align: center;
margin-bottom: 2rem;
box-shadow: 0 10px 25px -5px rgba(0,0,0,0.2);
position: relative;
overflow: hidden;
}
.premium-header::before {
content: "";
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.15) 0%, transparent 70%);
animation: rotate 20s linear infinite;
}
@keyframes gradient {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.premium-header h1 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
letter-spacing: -0.02em;
position: relative;
z-index: 2;
}
.premium-header p {
font-size: 1.1rem;
opacity: 0.9;
font-weight: 300;
position: relative;
z-index: 2;
}
/* Input styling – clean, modern */
.premium-input textarea {
border: 1px solid #e2e8f0 !important;
border-radius: 1rem !important;
padding: 0.75rem 1rem !important;
font-size: 1rem !important;
background: white !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.02) !important;
transition: all 0.2s !important;
width: 100% !important;
}
.premium-input textarea:focus {
border-color: #3182ce !important;
box-shadow: 0 0 0 3px rgba(49,130,206,0.1) !important;
outline: none !important;
}
/* Buttons – modern, with hover effects */
.btn-primary {
background: #3182ce;
color: white;
border: none;
padding: 0.6rem 1.5rem;
border-radius: 2rem;
font-weight: 500;
font-size: 1rem;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 4px 6px -1px rgba(49,130,206,0.3);
width: 100%;
display: inline-flex;
align-items: center;
justify-content: center;
}
.btn-primary:hover {
background: #2c5282;
transform: translateY(-1px);
box-shadow: 0 6px 8px -1px rgba(49,130,206,0.4);
}
.btn-secondary {
background: white;
color: #1a202c;
border: 1px solid #cbd5e0;
padding: 0.5rem 1.2rem;
border-radius: 2rem;
font-weight: 500;
font-size: 0.95rem;
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
justify-content: center;
}
.btn-secondary:hover {
background: #f7fafc;
border-color: #a0aec0;
}
/* Cards – glass effect */
.glass-card {
background: rgba(255,255,255,0.8);
backdrop-filter: blur(10px);
border-radius: 1.5rem;
padding: 1.5rem;
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
border: 1px solid rgba(255,255,255,0.5);
height: fit-content;
}
/* Plot cards */
.plot-card {
background: white;
border-radius: 1.2rem;
padding: 1rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
border: 1px solid #edf2f7;
transition: all 0.2s;
margin-bottom: 1rem;
}
.plot-card:hover {
box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1);
}
/* Live badge */
.live-badge {
background: #3182ce;
color: white;
padding: 0.4rem 1.2rem;
border-radius: 2rem;
font-size: 0.9rem;
font-weight: 500;
display: inline-block;
box-shadow: 0 2px 4px rgba(49,130,206,0.2);
margin: 0.5rem 0;
}
/* Slider styling */
.premium-slider {
background: white;
border-radius: 1rem;
padding: 0.5rem 1rem;
border: 1px solid #e2e8f0;
}
/* Responsive design */
@media (max-width: 768px) {
.gradio-container {
padding: 1rem;
margin: 0.5rem;
border-radius: 1.5rem;
}
.premium-header h1 {
font-size: 1.8rem;
}
.premium-header p {
font-size: 0.95rem;
}
.btn-primary, .btn-secondary {
width: 100%;
margin-top: 0.5rem;
}
/* Stack columns on mobile */
.gradio-row {
flex-direction: column !important;
}
.gradio-column {
width: 100% !important;
min-width: 100% !important;
}
}
/* Larger screens: allow more horizontal space */
@media (min-width: 1600px) {
.gradio-container {
max-width: 1800px;
padding: 2.5rem;
}
.premium-header h1 {
font-size: 3rem;
}
}
/* Fade-in animation */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in {
animation: fadeInUp 0.5s ease-out;
}
</style>
""")
# ==========================
# HEADER
# ==========================
gr.HTML(f"""
<div class="premium-header fade-in">
<h1>🚀 {BRAND_NAME}</h1>
<p>AI-Powered Market Intelligence • Real-Time Analysis • Strategic Insights</p>
</div>
""")
# ==========================
# INPUT ROW (NO VOICE)
# ==========================
with gr.Row(elem_classes="gradio-row"):
with gr.Column(scale=4, elem_classes="gradio-column"):
idea_input = gr.Textbox(
placeholder="Enter your startup idea... e.g., AI platform for personalized mental health coaching",
lines=2,
container=False,
elem_classes="premium-input"
)
with gr.Column(scale=1, min_width=120, elem_classes="gradio-column"):
analyze_btn = gr.Button("Analyze", elem_classes="btn-primary")
# ==========================
# STATES
# ==========================
result_state = gr.State()
radial_fig_state = gr.State()
years_state = gr.State()
# Live similarity badge
live_display = gr.HTML('<span class="live-badge">Live Similarity: N/A</span>')
# ==========================
# SUMMARY & JSON
# ==========================
with gr.Row(elem_classes="gradio-row"):
with gr.Column(scale=1, elem_classes="glass-card fade-in gradio-column"):
summary_output = gr.Markdown()
with gr.Column(scale=1, elem_classes="glass-card fade-in gradio-column"):
json_output = gr.Code(label="📄 Structured Report", language="json", lines=10)
# ==========================
# PLOT ROWS
# ==========================
# Row 1
with gr.Row(elem_classes="gradio-row"):
with gr.Column(elem_classes="plot-card gradio-column"):
radial_output = gr.Plot(label="🗺️ Strategic Opportunity Map")
with gr.Column(elem_classes="plot-card gradio-column"):
market_output = gr.Plot(label="🧠 3D Market Landscape")
with gr.Column(elem_classes="plot-card gradio-column"):
gauge_output = gr.Plot(label="🎯 Project Score")
# Row 2
with gr.Row(elem_classes="gradio-row"):
with gr.Column(elem_classes="plot-card gradio-column"):
kpi_output = gr.Plot(label="📊 KPI Panel")
with gr.Column(elem_classes="plot-card gradio-column"):
matrix_output = gr.Plot(label="🎯 Positioning Matrix")
# Row 3 (Growth with year slider)
with gr.Row(elem_classes="gradio-row"):
with gr.Column(elem_classes="plot-card gradio-column"):
year_slider = gr.Slider(
minimum=0, maximum=0, step=1, value=0,
label="Select Year", visible=False,
elem_classes="premium-slider"
)
growth_output = gr.Plot(label="📈 Growth by Year")
# Row 4
with gr.Row(elem_classes="gradio-row"):
with gr.Column(elem_classes="plot-card gradio-column"):
heat_output = gr.Plot(label="🔥 Market Heatmap")
with gr.Column(elem_classes="plot-card gradio-column"):
radar_opp_output = gr.Plot(label="⭐ Opportunity Radar")
# ==========================
# CONTROLS
# ==========================
with gr.Row(elem_classes="glass-card fade-in gradio-row"):
step_slider = gr.Slider(minimum=1, maximum=8, step=1, value=1, label="Reveal Steps", elem_classes="premium-slider")
reveal_btn = gr.Button("Reveal Next Step", elem_classes="btn-secondary")
live_btn = gr.Button("Refresh Live Similarity", elem_classes="btn-secondary")
pdf_btn = gr.Button("📥 Download PDF Report", elem_classes="btn-primary")
pdf_output = gr.File(label="Download PDF", visible=True)
confetti_html = gr.HTML()
# ==========================
# EVENT HANDLERS
# ==========================
def update_after_analysis(summary, json, radial, market, gauge,
kpi, matrix, growth, heat, radar_opp,
result, radial_fig, confetti, years):
if years and len(years) > 0:
return (summary, json, radial, market, gauge,
kpi, matrix, growth, heat, radar_opp,
result, radial_fig, confetti,
gr.update(minimum=0, maximum=len(years)-1, step=1,
value=0, visible=True, label=f"Year: {years[0]}"),
years)
else:
return (summary, json, radial, market, gauge,
kpi, matrix, growth, heat, radar_opp,
result, radial_fig, confetti,
gr.update(visible=False), years)
analyze_btn.click(
fn=full_pipeline,
inputs=idea_input,
outputs=[summary_output, json_output,
radial_output, market_output, gauge_output,
kpi_output, matrix_output, growth_output, heat_output, radar_opp_output,
result_state, radial_fig_state, confetti_html, years_state]
).then(
fn=update_after_analysis,
inputs=[summary_output, json_output,
radial_output, market_output, gauge_output,
kpi_output, matrix_output, growth_output, heat_output, radar_opp_output,
result_state, radial_fig_state, confetti_html, years_state],
outputs=[summary_output, json_output,
radial_output, market_output, gauge_output,
kpi_output, matrix_output, growth_output, heat_output, radar_opp_output,
result_state, radial_fig_state, confetti_html, year_slider, years_state]
).then(
fn=lambda rs: f"<span class='live-badge'>Live Similarity: {rs.get('similarity_score', 'N/A')}</span>",
inputs=result_state,
outputs=live_display
)
def on_year_change(result_state, slider_val, years_state):
if years_state and slider_val < len(years_state):
selected_year = years_state[slider_val]
return update_growth_by_year(result_state, selected_year)
return go.Figure()
year_slider.change(
fn=on_year_change,
inputs=[result_state, year_slider, years_state],
outputs=growth_output
)
def update_slider_label(slider_val, years_state):
if years_state and slider_val < len(years_state):
return gr.update(label=f"Year: {years_state[slider_val]}")
return gr.update(label="Select Year")
year_slider.change(
fn=update_slider_label,
inputs=[year_slider, years_state],
outputs=year_slider
)
step_slider.change(
fn=update_radial_and_state,
inputs=[result_state, step_slider],
outputs=[radial_output, radial_fig_state]
)
reveal_btn.click(
fn=increment_step,
inputs=step_slider,
outputs=step_slider
).then(
fn=update_radial_and_state,
inputs=[result_state, step_slider],
outputs=[radial_output, radial_fig_state]
)
live_btn.click(
fn=update_live_score,
inputs=result_state,
outputs=live_display
)
pdf_btn.click(
fn=prepare_pdf,
inputs=[result_state, radial_fig_state],
outputs=pdf_output
)
# ==========================
# FOOTER
# ==========================
gr.HTML("""
<div style="text-align: center; margin-top: 3rem; padding: 1rem; color: #718096; font-size: 0.9rem;">
<p>Powered by Groq LLama 3.3 • FAISS • Sentence‑Transformers</p>
</div>
""")
if __name__ == "__main__":
app.launch(server_name="0.0.0.0", server_port=7860)