DetectiveShadow commited on
Commit
62f83ce
·
verified ·
1 Parent(s): 5ddc99e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +244 -0
app.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ # ------------------------------
4
+ # Data (minimal, in-file)
5
+ # ------------------------------
6
+ RUBRIC_WEIGHTS = {
7
+ "grit": 0.18, "risk": 0.12, "time_per_week": 0.14, "runway_months": 0.16,
8
+ "customer_access": 0.12, "validation_skill": 0.10, "ops_skill": 0.08, "finance_skill": 0.10
9
+ }
10
+
11
+ PATHS = {
12
+ "tech": [
13
+ {"path":"Micro-SaaS","difficulty":3,"benefits":"High margins, scalable, global reach",
14
+ "skills":["customer interviews","MVP scoping","pricing","analytics"],
15
+ "starter":["Define niche pain","5–10 interviews","Landing page test","Iterate"]},
16
+ {"path":"No-code automation agency","difficulty":2,"benefits":"Service revenue funds experiments",
17
+ "skills":["workflow mapping","Zapier/Make","sales","onboarding"],
18
+ "starter":["Pick niche","3 pilot clients","SOPs","Retainers"]},
19
+ {"path":"Educational apps","difficulty":3,"benefits":"Recurring revenue via schools/parents",
20
+ "skills":["curriculum","UX","distribution partnerships"],
21
+ "starter":["Niche curriculum","Prototype","Parent/teacher tests"]}
22
+ ],
23
+ "cooking": [
24
+ {"path":"Meal-prep delivery","difficulty":4,"benefits":"Tangible product; recurring subs",
25
+ "skills":["food safety","menu costing","logistics","support"],
26
+ "starter":["Permits","Menu tests","Delivery loop","Subscriptions"]},
27
+ {"path":"Cooking classes","difficulty":2,"benefits":"Low capex; community",
28
+ "skills":["lesson design","facilitation","marketing"],
29
+ "starter":["Pilot class","Testimonials","Cohorts","Bundles"]}
30
+ ],
31
+ "education": [
32
+ {"path":"Tutoring niche","difficulty":2,"benefits":"Fast validation; referrals",
33
+ "skills":["subject mastery","scheduling","sales"],
34
+ "starter":["ICP","5 trial students","Packages","Referrals"]}
35
+ ],
36
+ "fitness": [
37
+ {"path":"Online coaching","difficulty":2,"benefits":"Low overhead; trust",
38
+ "skills":["programming","accountability","content"],
39
+ "starter":["Niche offer","Clients 1–3","Testimonials","Packages"]}
40
+ ],
41
+ "content": [
42
+ {"path":"Newsletter + paid tiers","difficulty":2,"benefits":"Audience compounds",
43
+ "skills":["editorial","conversion","email ops"],
44
+ "starter":["Niche POV","Weekly cadence","Lead magnet","Feedback loop"]}
45
+ ],
46
+ "games": [
47
+ {"path":"Indie premium","difficulty":4,"benefits":"Creative control; wishlists matter",
48
+ "skills":["scope control","playtesting","marketing"],
49
+ "starter":["Core loop","Vertical slice","Store page","Playtests"]}
50
+ ],
51
+ "fashion": [
52
+ {"path":"Print-on-demand niche","difficulty":2,"benefits":"Low inventory risk",
53
+ "skills":["design","niche research","ads"],
54
+ "starter":["Mockups","POD store","Ad test","Iterate"]}
55
+ ]
56
+ }
57
+
58
+ SYSTEM_PROMPT = (
59
+ "You are a friendly tutor for Entrepreneurial Readiness and Paths. "
60
+ "Be practical and concise. If the user ran the assessment or path explorer, use that info."
61
+ )
62
+
63
+ # ------------------------------
64
+ # Readiness scoring (0–100)
65
+ # ------------------------------
66
+ def compute_readiness(payload):
67
+ # normalize
68
+ grit = payload["grit"]/10
69
+ risk = payload["risk"]/10
70
+ time_norm = min(payload["time_per_week"], 40)/40
71
+ runway_norm = min(payload["runway_months"], 24)/24
72
+ customer_access = payload["customer_access"]/10
73
+ validation_skill = payload["validation_skill"]/10
74
+ ops_skill = payload["ops_skill"]/10
75
+ finance_skill = payload["finance_skill"]/10
76
+
77
+ score = 100 * (
78
+ RUBRIC_WEIGHTS["grit"]*grit +
79
+ RUBRIC_WEIGHTS["risk"]*risk +
80
+ RUBRIC_WEIGHTS["time_per_week"]*time_norm +
81
+ RUBRIC_WEIGHTS["runway_months"]*runway_norm +
82
+ RUBRIC_WEIGHTS["customer_access"]*customer_access +
83
+ RUBRIC_WEIGHTS["validation_skill"]*validation_skill +
84
+ RUBRIC_WEIGHTS["ops_skill"]*ops_skill +
85
+ RUBRIC_WEIGHTS["finance_skill"]*finance_skill
86
+ )
87
+ score = int(round(score))
88
+ tier = "Starter" if score < 55 else ("Building" if score < 75 else "Launch-ready")
89
+
90
+ tips = []
91
+ if runway_norm < 0.35: tips.append("Build 6–12 months runway or run as a side project.")
92
+ if time_norm < 0.5: tips.append("Protect ~8–12 hrs/week for interviews & experiments.")
93
+ if customer_access < 0.6: tips.append("Line up 10–20 target users for interviews.")
94
+ if validation_skill < 0.6: tips.append("Run a demand test (landing page/preorder) before building.")
95
+ if finance_skill < 0.5: tips.append("Track cash runway monthly.")
96
+ if ops_skill < 0.5: tips.append("Create a weekly ops checklist to reduce chaos.")
97
+ return {"score":score, "tier":tier, "tips":tips}
98
+
99
+ # ------------------------------
100
+ # Path suggestions helper
101
+ # ------------------------------
102
+ def suggest_paths(interests, experience):
103
+ results = []
104
+ for domain in interests or []:
105
+ for item in PATHS.get(domain, []):
106
+ diff = item["difficulty"]
107
+ # adjust for experience
108
+ if experience == "beginner": diff = min(5, diff + 1)
109
+ if experience == "advanced": diff = max(1, diff - 1)
110
+ results.append({
111
+ "domain": domain,
112
+ "path": item["path"],
113
+ "difficulty_1to5": diff,
114
+ "benefits": item["benefits"],
115
+ "key_skills": item["skills"],
116
+ "starter_plan": item["starter"],
117
+ })
118
+ # keep it short
119
+ return results[:6]
120
+
121
+ # ------------------------------
122
+ # Simple rule-based "chat"
123
+ # ------------------------------
124
+ def local_reply(user_msg, history, assessment_state, path_state):
125
+ text = (user_msg or "").lower()
126
+
127
+ # reference readiness
128
+ if "score" in text or "ready" in text or "readiness" in text:
129
+ if isinstance(assessment_state, dict) and "score" in assessment_state:
130
+ s = assessment_state["score"]
131
+ t = assessment_state.get("tier", "?")
132
+ return f"Your readiness is **{s}/100 ({t})**. Top tip: {assessment_state.get('tips', ['Run small demand tests first.'])[0]}"
133
+ else:
134
+ return "Run **Assess readiness** on the right, then ask me again 🙂"
135
+
136
+ # reference paths
137
+ if "path" in text or "idea" in text or "domain" in text:
138
+ if isinstance(path_state, dict) and path_state.get("suggestions"):
139
+ sug = path_state["suggestions"][0]
140
+ return (f"Try **{sug['domain']} → {sug['path']}**. "
141
+ f"Difficulty ~{sug['difficulty_1to5']}/5. "
142
+ f"Benefits: {sug['benefits']}. "
143
+ f"First steps: {', '.join(sug['starter_plan'][:3])}. Want more options?")
144
+ else:
145
+ return "Pick a few interests under **Path Explorer**, hit **Suggest paths**, then ask me which to start with."
146
+
147
+ # detect domain mention
148
+ for domain in PATHS.keys():
149
+ if domain in text:
150
+ first = PATHS[domain][0]
151
+ return (f"In **{domain}**, consider **{first['path']}** "
152
+ f"(difficulty {first['difficulty']}/5). "
153
+ f"Why it can pay off: {first['benefits']}. "
154
+ f"Starter plan: {', '.join(first['starter'][:3])}.")
155
+ # default helpful tip
156
+ return ("Start with a small **demand test**: a landing page and 5–10 user interviews. "
157
+ "Measure real intent (signups/preorders) before you build.")
158
+
159
+ # ------------------------------
160
+ # Gradio UI
161
+ # ------------------------------
162
+ with gr.Blocks(theme="soft", fill_height=True) as demo:
163
+ gr.Markdown("## Entrepreneurial Tutor (no external model)\nAsk about readiness or paths. Use the tools on the right to feed me context.")
164
+
165
+ with gr.Row():
166
+ # Chat
167
+ with gr.Column(scale=3):
168
+ chat = gr.Chatbot(type="tuples", height=460)
169
+ with gr.Row():
170
+ msg = gr.Textbox(placeholder="Ask about Entrepreneurial paths or readiness…", scale=4)
171
+ send = gr.Button("Send", variant="primary")
172
+
173
+ # Tools
174
+ with gr.Column(scale=2):
175
+ gr.Markdown("### Quick Readiness Assessment")
176
+ grit = gr.Slider(0,10, value=6, step=1, label="Grit / persistence")
177
+ risk = gr.Slider(0,10, value=5, step=1, label="Comfort with risk")
178
+ time_per_week = gr.Slider(0,40, value=8, step=1, label="Time available per week (hrs)")
179
+ runway_months = gr.Slider(0,24, value=3, step=1, label="Financial runway (months)")
180
+ customer_access = gr.Slider(0,10, value=5, step=1, label="Access to target customers")
181
+ validation_skill = gr.Slider(0,10, value=4, step=1, label="Validation / testing skill")
182
+ ops_skill = gr.Slider(0,10, value=4, step=1, label="Operations / delivery skill")
183
+ finance_skill = gr.Slider(0,10, value=3, step=1, label="Finance / budgeting")
184
+
185
+ assess_btn = gr.Button("Assess readiness")
186
+ assessment_state = gr.State({})
187
+ assess_out = gr.JSON(label="Assessment Result")
188
+
189
+ gr.Markdown("### Path Explorer")
190
+ interests = gr.CheckboxGroup(
191
+ choices=list(PATHS.keys()),
192
+ value=["tech"],
193
+ label="Interests / domains"
194
+ )
195
+ experience = gr.Radio(
196
+ choices=["beginner","intermediate","advanced"],
197
+ value="beginner",
198
+ label="Experience level"
199
+ )
200
+ suggest_btn = gr.Button("Suggest paths")
201
+ path_state = gr.State({})
202
+ paths_out = gr.JSON(label="Suggested Paths")
203
+
204
+ # Wiring
205
+ def do_assess(grit, risk, time_per_week, runway_months, customer_access, validation_skill, ops_skill, finance_skill):
206
+ payload = {
207
+ "grit":grit,"risk":risk,"time_per_week":time_per_week,"runway_months":runway_months,
208
+ "customer_access":customer_access,"validation_skill":validation_skill,
209
+ "ops_skill":ops_skill,"finance_skill":finance_skill
210
+ }
211
+ result = compute_readiness(payload)
212
+ return result, result # show + store
213
+
214
+ assess_btn.click(
215
+ do_assess,
216
+ inputs=[grit, risk, time_per_week, runway_months, customer_access, validation_skill, ops_skill, finance_skill],
217
+ outputs=[assess_out, assessment_state]
218
+ )
219
+
220
+ def do_paths(interests, experience):
221
+ res = suggest_paths(interests, experience)
222
+ state = {"interests": interests, "experience": experience, "suggestions": res}
223
+ return res, state
224
+
225
+ suggest_btn.click(
226
+ do_paths,
227
+ inputs=[interests, experience],
228
+ outputs=[paths_out, path_state]
229
+ )
230
+
231
+ def on_send(user_message, history, assessment_state, path_state):
232
+ if not user_message:
233
+ return gr.update(), history
234
+ reply = local_reply(user_message, history, assessment_state, path_state)
235
+ return "", (history or []) + [[user_message, reply]]
236
+
237
+ send.click(
238
+ on_send,
239
+ inputs=[msg, chat, assessment_state, path_state],
240
+ outputs=[msg, chat]
241
+ )
242
+
243
+ if __name__ == "__main__":
244
+ demo.launch()