DetectiveShadow's picture
Update app.py
2a1c80f verified
# app.py
import gradio as gr
import json
SYSTEM_PROMPT = (
"You are a friendly tutor for Entrepreneurial Readiness and Paths. "
"Be practical and concise. If the user ran the assessment or path explorer, use that info."
)
# =========================
# Domains (unchanged)
# =========================
PATHS = {
"tech": [
{"path":"Micro-SaaS","difficulty":3,"benefits":"High margins, scalable, global reach",
"skills":["customer interviews","MVP scoping","pricing","analytics"],
"starter":["Define niche pain","5–10 interviews","Landing page test","Iterate"]},
{"path":"No-code automation agency","difficulty":2,"benefits":"Service revenue funds experiments",
"skills":["workflow mapping","Zapier/Make","sales","onboarding"],
"starter":["Pick niche","3 pilot clients","SOPs","Retainers"]},
{"path":"Educational apps","difficulty":3,"benefits":"Recurring revenue via schools/parents",
"skills":["curriculum","UX","distribution partnerships"],
"starter":["Niche curriculum","Prototype","Parent/teacher tests"]}
],
"cooking": [
{"path":"Meal-prep delivery","difficulty":4,"benefits":"Tangible product; recurring subs",
"skills":["food safety","menu costing","logistics","support"],
"starter":["Permits","Menu tests","Delivery loop","Subscriptions"]},
{"path":"Cooking classes","difficulty":2,"benefits":"Low capex; community",
"skills":["lesson design","facilitation","marketing"],
"starter":["Pilot class","Testimonials","Cohorts","Bundles"]}
],
"education": [
{"path":"Tutoring niche","difficulty":2,"benefits":"Fast validation; referrals",
"skills":["subject mastery","scheduling","sales"],
"starter":["ICP","5 trial students","Packages","Referrals"]}
],
"fitness": [
{"path":"Online coaching","difficulty":2,"benefits":"Low overhead; trust",
"skills":["programming","accountability","content"],
"starter":["Niche offer","Clients 1–3","Testimonials","Packages"]}
],
"content": [
{"path":"Newsletter + paid tiers","difficulty":2,"benefits":"Audience compounds",
"skills":["editorial","conversion","email ops"],
"starter":["Niche POV","Weekly cadence","Lead magnet","Feedback loop"]}
],
"games": [
{"path":"Indie premium","difficulty":4,"benefits":"Creative control; wishlists matter",
"skills":["scope control","playtesting","marketing"],
"starter":["Core loop","Vertical slice","Store page","Playtests"]}
],
"fashion": [
{"path":"Print-on-demand niche","difficulty":2,"benefits":"Low inventory risk",
"skills":["design","niche research","ads"],
"starter":["Mockups","POD store","Ad test","Iterate"]}
]
}
# =========================
# Readiness factors (your list)
# =========================
WEIGHTS = {
"savings": 0.10,
"income": 0.08,
"bills": 0.08, # inverse
"entertainment": 0.04, # inverse
"assets": 0.06,
"sales_skills": 0.14,
"confidence": 0.10,
"dependents": 0.06, # inverse
"age": 0.04, # very small, symmetric
"idea_level": 0.10,
"entrepreneurial_experience": 0.10,
"risk": 0.10,
}
CAP = {
"savings": 20000, # USD
"income": 8000, # USD / month
"bills": 5000, # USD / month (inverse)
"entertainment": 1000, # USD / month (inverse)
"assets": 100000, # USD (liquid-ish)
"dependents_max": 3, # 0..3+ (inverse)
}
IDEA_LEVEL_MAP = {
"none": 0.0,
"exploring": 0.25,
"validated_problem": 0.5,
"prototype": 0.75,
"mvp_with_users": 1.0,
}
def _nz(x, default=0.0):
try:
return float(x) if x is not None else float(default)
except Exception:
return float(default)
def _clamp01(v): return max(0.0, min(1.0, float(v)))
def _direct_norm(v, cap): return _clamp01(_nz(v) / float(cap))
def _inverse_norm(v, cap): return 1.0 - _direct_norm(v, cap)
def _age_norm(age):
"""Mild symmetric curve centered ~35; tiny effect (≤0.2)."""
a = _nz(age, 35)
penalty = min(abs(a - 35.0) / 35.0, 1.0) * 0.2
return _clamp01(1.0 - penalty)
def _dependents_norm(dep):
d = max(0, int(_nz(dep, 0)))
base = 1.0 - min(d / float(CAP["dependents_max"]), 1.0)
return _clamp01(base)
# ------------------------------
# Readiness scoring (0–100) using your factors
# ------------------------------
def compute_readiness(payload):
n = {
"savings": _direct_norm(payload["savings"], CAP["savings"]),
"income": _direct_norm(payload["income"], CAP["income"]),
"bills": _inverse_norm(payload["bills"], CAP["bills"]),
"entertainment": _inverse_norm(payload["entertainment"], CAP["entertainment"]),
"assets": _direct_norm(payload["assets"], CAP["assets"]),
"sales_skills": _clamp01(_nz(payload["sales_skills"]) / 10.0),
"confidence": _clamp01(_nz(payload["confidence"]) / 10.0),
"dependents": _dependents_norm(payload["dependents"]),
"age": _age_norm(payload["age"]),
"idea_level": IDEA_LEVEL_MAP.get(payload["idea_level"], 0.0),
"entrepreneurial_experience": _clamp01(_nz(payload["entrepreneurial_experience"]) / 10.0),
"risk": _clamp01(_nz(payload["risk"]) / 10.0),
}
total = sum(WEIGHTS[k] * n[k] for k in WEIGHTS.keys())
score = int(round(100 * total))
tier = "Starter" if score < 55 else ("Building" if score < 75 else "Launch-ready")
tips = []
# Finance levers
if n["savings"] < 0.5 or n["assets"] < 0.4:
tips.append("Run a 60–90 day runway sprint: reduce burn and build a buffer.")
if n["bills"] < 0.6 or n["entertainment"] < 0.6:
tips.append("Do a weekly expense audit and a 30-day no-spend on non-essentials.")
if n["income"] < 0.5:
tips.append("Spin up a simple service for cash: 3 outreach/day → 2 pilot clients.")
# Skill/mindset levers
if n["sales_skills"] < 0.6:
tips.append("Do 20 cold DMs/day for 5 days; refine your offer script.")
if n["confidence"] < 0.6:
tips.append("Post build-in-public for 10 days to desensitize and attract allies.")
if n["entrepreneurial_experience"] < 0.5:
tips.append("Run a 2-week micro-project: interviews → small paid pilot.")
if n["idea_level"] < 0.5:
tips.append("Advance one notch: exploring → validated problem → prototype → MVP.")
# Risk
if n["risk"] < 0.5:
tips.append("Set a ‘risk budget’: small, reversible tests with clear stop-lines.")
return {"score": score, "tier": tier, "tips": tips[:5], "normalized": n}
# ------------------------------
# Path suggestions helper (unchanged)
# ------------------------------
def suggest_paths(interests, experience):
results = []
for domain in interests or []:
for item in PATHS.get(domain, []):
diff = item["difficulty"]
if experience == "beginner": diff = min(5, diff + 1)
if experience == "advanced": diff = max(1, diff - 1)
results.append({
"domain": domain,
"path": item["path"],
"difficulty_1to5": diff,
"benefits": item["benefits"],
"key_skills": item["skills"],
"starter_plan": item["starter"],
})
return results[:6]
# ------------------------------
# Simple rule-based "chat"
# ------------------------------
def local_reply(user_msg, history, assessment_state, path_state):
text = (user_msg or "").lower()
if "score" in text or "ready" in text or "readiness" in text:
if isinstance(assessment_state, dict) and "score" in assessment_state:
s = assessment_state["score"]
t = assessment_state.get("tier", "?")
tip = (assessment_state.get("tips") or ["Run small demand tests first."])[0]
return f"Your readiness is **{s}/100 ({t})**. Top tip: {tip}"
else:
return "Run **Assess readiness** on the right, then ask me again 🙂"
if "path" in text or "idea" in text or "domain" in text:
if isinstance(path_state, dict) and path_state.get("suggestions"):
sug = path_state["suggestions"][0]
return (f"Try **{sug['domain']}{sug['path']}**. "
f"Difficulty ~{sug['difficulty_1to5']}/5. "
f"Benefits: {sug['benefits']}. "
f"First steps: {', '.join(sug['starter_plan'][:3])}. Want more options?")
else:
return "Pick a few interests under **Path Explorer**, hit **Suggest paths**, then ask me which to start with."
for domain in PATHS.keys():
if domain in text:
first = PATHS[domain][0]
return (f"In **{domain}**, consider **{first['path']}** "
f"(difficulty {first['difficulty']}/5). "
f"Why it can pay off: {first['benefits']}. "
f"Starter plan: {', '.join(first['starter'][:3])}.")
return ("Start with a small **demand test**: a landing page and 5–10 user interviews. "
"Measure real intent (signups/preorders) before you build.")
# ------------------------------
# Gradio UI
# ------------------------------
with gr.Blocks(theme="soft", fill_height=True) as demo:
gr.Markdown("## Entrepreneurial Tutor")
with gr.Row():
# Chat
with gr.Column(scale=3):
chat = gr.Chatbot(type="tuples", height=460)
with gr.Row():
msg = gr.Textbox(placeholder="Ask about Entrepreneurial paths or readiness…", scale=4)
send = gr.Button("Send", variant="primary")
# Tools
with gr.Column(scale=2):
gr.Markdown("### Quick Readiness Assessment (your factors)")
with gr.Row():
savings = gr.Number(value=2000, label="Savings (USD)", precision=0)
income = gr.Number(value=2500, label="Income/mo (USD)", precision=0)
with gr.Row():
bills = gr.Number(value=1500, label="Bills/mo (USD)", precision=0)
entertainment = gr.Number(value=200, label="Entertainment/mo (USD)", precision=0)
assets = gr.Number(value=0, label="Assets (USD)", precision=0)
sales_skills = gr.Slider(0,10, value=5, step=1, label="Sales skills (0–10)")
confidence = gr.Slider(0,10, value=5, step=1, label="Confidence (0–10)")
dependents = gr.Slider(0,6, value=0, step=1, label="Dependents (count)")
age = gr.Slider(15,80, value=25, step=1, label="Age")
idea_level = gr.Radio(
choices=list(IDEA_LEVEL_MAP.keys()),
value="exploring",
label="Idea level"
)
entrepreneurial_experience = gr.Slider(0,10, value=3, step=1, label="Entrepreneurial experience (0–10)")
risk = gr.Slider(0,10, value=5, step=1, label="Risk tolerance (0–10)")
assess_btn = gr.Button("Assess readiness")
assessment_state = gr.State({})
assess_out = gr.JSON(label="Assessment Result")
gr.Markdown("### Path Explorer (domains)")
interests = gr.CheckboxGroup(
choices=list(PATHS.keys()),
value=["tech"],
label="Interests / domains"
)
experience = gr.Radio(
choices=["beginner","intermediate","advanced"],
value="beginner",
label="Experience level"
)
suggest_btn = gr.Button("Suggest paths")
path_state = gr.State({})
paths_out = gr.JSON(label="Suggested Paths")
# Wiring
def do_assess(savings, income, bills, entertainment, assets,
sales_skills, confidence, dependents, age,
idea_level, entrepreneurial_experience, risk):
payload = {
"savings": savings, "income": income, "bills": bills, "entertainment": entertainment, "assets": assets,
"sales_skills": sales_skills, "confidence": confidence, "dependents": dependents, "age": age,
"idea_level": idea_level, "entrepreneurial_experience": entrepreneurial_experience, "risk": risk
}
result = compute_readiness(payload)
return result, result # show + store
assess_btn.click(
do_assess,
inputs=[savings, income, bills, entertainment, assets,
sales_skills, confidence, dependents, age,
idea_level, entrepreneurial_experience, risk],
outputs=[assess_out, assessment_state]
)
def do_paths(interests, experience):
res = suggest_paths(interests, experience)
state = {"interests": interests, "experience": experience, "suggestions": res}
return res, state
suggest_btn.click(
do_paths,
inputs=[interests, experience],
outputs=[paths_out, path_state]
)
def on_send(user_message, history, assessment_state, path_state):
if not user_message:
return gr.update(), history
reply = local_reply(user_message, history, assessment_state, path_state)
return "", (history or []) + [[user_message, reply]]
send.click(
on_send,
inputs=[msg, chat, assessment_state, path_state],
outputs=[msg, chat]
)
if __name__ == "__main__":
demo.launch()