Start-Up_Viability_Agent / startup_coach_personas.py
Navada25's picture
Update startup_coach_personas.py - Voice Streaming & AI Coaching Features
beb208c verified
"""
AI-Powered Startup Coach Personas for NAVADA
Provides specialized coaching expertise across different startup domains
"""
import json
import logging
import random
from typing import Dict, Any, List, Optional
from datetime import datetime
import openai
from openai import AsyncOpenAI
import os
logger = logging.getLogger(__name__)
class StartupCoachPersonas:
"""Manages AI-powered startup coaching personas with specialized expertise"""
def __init__(self):
self.client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.current_session = None
self.persona_history = {}
def get_available_personas(self) -> Dict[str, Dict[str, Any]]:
"""Get all available coaching personas with their specializations"""
return {
"sarah_strategic": {
"name": "Sarah Chen - Strategic Advisor",
"expertise": ["Business Strategy", "Market Entry", "Competitive Analysis", "Growth Planning"],
"background": "Former McKinsey consultant, 3 successful exits as startup founder",
"personality": "Analytical, direct, strategic thinker",
"speaking_style": "Data-driven with real-world examples",
"avatar": "πŸ‘©β€πŸ’Ό",
"color": "#4F46E5"
},
"marcus_technical": {
"name": "Marcus Rodriguez - CTO Advisor",
"expertise": ["Technical Architecture", "MVP Development", "Tech Stack Selection", "Scalability"],
"background": "Ex-Google Principal Engineer, built platforms for 100M+ users",
"personality": "Pragmatic, detail-oriented, solution-focused",
"speaking_style": "Technical but accessible, focuses on implementation",
"avatar": "πŸ‘¨β€πŸ’»",
"color": "#059669"
},
"elena_financial": {
"name": "Elena Thompson - Finance Expert",
"expertise": ["Financial Modeling", "Fundraising", "Valuation", "Unit Economics"],
"background": "Former VC Partner at Sequoia, CFO at 2 unicorns",
"personality": "Meticulous, numbers-focused, investor mindset",
"speaking_style": "Precise financial language with clear metrics",
"avatar": "πŸ‘©β€πŸ’°",
"color": "#DC2626"
},
"david_marketing": {
"name": "David Kim - Growth Hacker",
"expertise": ["Customer Acquisition", "Product Marketing", "Growth Strategies", "User Retention"],
"background": "VP Growth at Airbnb, scaled 3 startups from 0 to 10M users",
"personality": "Creative, experimental, customer-obsessed",
"speaking_style": "Energetic with growth hacks and case studies",
"avatar": "πŸ‘¨β€πŸš€",
"color": "#7C3AED"
},
"lisa_legal": {
"name": "Lisa Johnson - Legal & Compliance",
"expertise": ["Corporate Structure", "IP Protection", "Regulatory Compliance", "Contracts"],
"background": "Former startup lawyer at Wilson Sonsini, in-house counsel at Meta",
"personality": "Thorough, risk-aware, protective",
"speaking_style": "Clear legal guidance with practical implications",
"avatar": "πŸ‘©β€βš–οΈ",
"color": "#0891B2"
},
"alex_operations": {
"name": "Alex Chen - Operations Guru",
"expertise": ["Operations Scaling", "Process Optimization", "Team Building", "Efficiency"],
"background": "COO at 3 successful startups, operations consultant",
"personality": "Systematic, efficiency-focused, team-oriented",
"speaking_style": "Process-driven with actionable frameworks",
"avatar": "πŸ‘¨β€πŸ”§",
"color": "#EA580C"
}
}
async def initialize_coaching_session(self, persona_id: str, startup_context: Dict[str, Any]) -> Dict[str, Any]:
"""Initialize a coaching session with a specific persona"""
try:
personas = self.get_available_personas()
if persona_id not in personas:
return {"status": "error", "message": "Invalid persona ID"}
persona = personas[persona_id]
# Create coaching session context
session_id = f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{persona_id}"
self.current_session = {
"session_id": session_id,
"persona_id": persona_id,
"persona": persona,
"startup_context": startup_context,
"conversation_history": [],
"created_at": datetime.now().isoformat(),
"focus_areas": [],
"action_items": []
}
# Initialize persona history if not exists
if persona_id not in self.persona_history:
self.persona_history[persona_id] = []
return {
"status": "success",
"session_id": session_id,
"persona": persona,
"welcome_message": await self._generate_welcome_message(persona, startup_context)
}
except Exception as e:
logger.error(f"Error initializing coaching session: {e}")
return {"status": "error", "message": str(e)}
async def _generate_welcome_message(self, persona: Dict[str, Any], startup_context: Dict[str, Any]) -> str:
"""Generate personalized welcome message from the persona"""
try:
system_prompt = f"""
You are {persona['name']}, a startup advisor with expertise in {', '.join(persona['expertise'])}.
Background: {persona['background']}
Personality: {persona['personality']}
Speaking Style: {persona['speaking_style']}
Generate a warm, personalized welcome message for this startup founder. Keep it under 150 words.
Be specific about how you can help based on your expertise and their startup context.
"""
user_prompt = f"""
Startup Context:
- Industry: {startup_context.get('industry', 'Not specified')}
- Stage: {startup_context.get('stage', 'Idea stage')}
- Team Size: {startup_context.get('team_size', 'Solo founder')}
- Current Challenge: {startup_context.get('current_challenge', 'General guidance needed')}
- Brief Description: {startup_context.get('description', 'Early stage startup')}
Generate a welcome message that shows you understand their situation and how you can specifically help.
"""
response = await self.client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
max_tokens=200,
temperature=0.7
)
return response.choices[0].message.content
except Exception as e:
logger.error(f"Error generating welcome message: {e}")
return f"Hi! I'm {persona['name']}. I'm excited to help you with your startup journey!"
async def get_coaching_advice(self, query: str, context: Dict[str, Any] = None) -> Dict[str, Any]:
"""Get specialized coaching advice from the current persona"""
try:
if not self.current_session:
return {"status": "error", "message": "No active coaching session"}
persona = self.current_session["persona"]
startup_context = self.current_session["startup_context"]
# Build conversation context
conversation_history = self.current_session["conversation_history"][-5:] # Last 5 exchanges
system_prompt = f"""
You are {persona['name']}, a startup advisor specializing in {', '.join(persona['expertise'])}.
Background: {persona['background']}
Personality: {persona['personality']}
Speaking Style: {persona['speaking_style']}
Provide specific, actionable advice based on your expertise. Always include:
1. Direct answer to their question
2. Specific next steps
3. Potential risks/considerations
4. Resources or tools to help
Keep responses focused and under 300 words unless complex analysis is needed.
"""
# Prepare context for the AI
context_text = f"""
Startup Context:
{json.dumps(startup_context, indent=2)}
Previous Conversation:
{json.dumps(conversation_history, indent=2) if conversation_history else "None"}
Current Question: {query}
"""
if context:
context_text += f"\nAdditional Context: {json.dumps(context, indent=2)}"
response = await self.client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": context_text}
],
max_tokens=500,
temperature=0.7
)
advice = response.choices[0].message.content
# Update conversation history
self.current_session["conversation_history"].append({
"timestamp": datetime.now().isoformat(),
"query": query,
"advice": advice,
"context": context
})
# Extract action items if any
action_items = await self._extract_action_items(advice)
if action_items:
self.current_session["action_items"].extend(action_items)
return {
"status": "success",
"advice": advice,
"persona": persona["name"],
"expertise_areas": persona["expertise"],
"action_items": action_items,
"session_id": self.current_session["session_id"]
}
except Exception as e:
logger.error(f"Error getting coaching advice: {e}")
return {"status": "error", "message": str(e)}
async def _extract_action_items(self, advice_text: str) -> List[str]:
"""Extract actionable items from coaching advice"""
try:
system_prompt = """
Extract specific, actionable items from this coaching advice.
Return only concrete next steps the founder should take.
Format as a JSON list of strings.
If no clear action items, return empty list.
"""
response = await self.client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": advice_text}
],
max_tokens=200,
temperature=0.3
)
result = response.choices[0].message.content
try:
return json.loads(result)
except json.JSONDecodeError:
return []
except Exception as e:
logger.error(f"Error extracting action items: {e}")
return []
async def get_persona_handoff_recommendation(self, current_query: str) -> Dict[str, Any]:
"""Recommend which persona would be best suited for the current query"""
try:
personas = self.get_available_personas()
system_prompt = """
Analyze the user's query and recommend the most suitable startup advisor persona.
Consider the expertise areas and which advisor would provide the most valuable insights.
Return your recommendation as JSON with:
{
"recommended_persona": "persona_id",
"confidence": 0.8,
"reasoning": "Why this persona is best suited",
"alternative": "backup_persona_id"
}
"""
personas_info = {pid: {"name": p["name"], "expertise": p["expertise"]}
for pid, p in personas.items()}
user_prompt = f"""
Available Personas:
{json.dumps(personas_info, indent=2)}
User Query: {current_query}
Current Session: {self.current_session["persona"]["name"] if self.current_session else "None"}
"""
response = await self.client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
max_tokens=200,
temperature=0.3
)
result = json.loads(response.choices[0].message.content)
result["current_persona"] = self.current_session["persona_id"] if self.current_session else None
return {"status": "success", **result}
except Exception as e:
logger.error(f"Error getting handoff recommendation: {e}")
return {"status": "error", "message": str(e)}
async def switch_persona(self, new_persona_id: str, handoff_context: str = None) -> Dict[str, Any]:
"""Switch to a different coaching persona with context handoff"""
try:
personas = self.get_available_personas()
if new_persona_id not in personas:
return {"status": "error", "message": "Invalid persona ID"}
# Prepare handoff summary if switching from another persona
handoff_summary = None
if self.current_session:
handoff_summary = await self._generate_handoff_summary()
# Store current session in history
if self.current_session:
persona_id = self.current_session["persona_id"]
self.persona_history[persona_id].append({
**self.current_session,
"ended_at": datetime.now().isoformat()
})
# Initialize new session
startup_context = self.current_session["startup_context"] if self.current_session else {}
if handoff_context:
startup_context["handoff_context"] = handoff_context
new_session = await self.initialize_coaching_session(new_persona_id, startup_context)
if new_session["status"] == "success":
# Add handoff summary to new session
if handoff_summary:
new_session["handoff_summary"] = handoff_summary
return new_session
except Exception as e:
logger.error(f"Error switching persona: {e}")
return {"status": "error", "message": str(e)}
async def _generate_handoff_summary(self) -> str:
"""Generate a handoff summary for persona transitions"""
try:
if not self.current_session or not self.current_session["conversation_history"]:
return "No previous conversation context."
system_prompt = """
Create a concise handoff summary for transitioning between startup advisors.
Include key discussion points, decisions made, and context the new advisor needs.
Keep it under 150 words and focus on actionable insights.
"""
conversation_summary = {
"persona": self.current_session["persona"]["name"],
"expertise": self.current_session["persona"]["expertise"],
"key_discussions": self.current_session["conversation_history"][-3:],
"action_items": self.current_session["action_items"]
}
response = await self.client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": json.dumps(conversation_summary, indent=2)}
],
max_tokens=200,
temperature=0.5
)
return response.choices[0].message.content
except Exception as e:
logger.error(f"Error generating handoff summary: {e}")
return "Error generating handoff summary."
def get_session_analytics(self) -> Dict[str, Any]:
"""Get analytics about coaching sessions"""
try:
total_sessions = sum(len(sessions) for sessions in self.persona_history.values())
persona_usage = {
persona_id: len(sessions)
for persona_id, sessions in self.persona_history.items()
}
most_used_persona = max(persona_usage.items(), key=lambda x: x[1]) if persona_usage else None
return {
"total_sessions": total_sessions,
"active_session": bool(self.current_session),
"current_persona": self.current_session["persona"]["name"] if self.current_session else None,
"persona_usage": persona_usage,
"most_used_persona": most_used_persona[0] if most_used_persona else None,
"available_personas": len(self.get_available_personas())
}
except Exception as e:
logger.error(f"Error getting session analytics: {e}")
return {"status": "error", "message": str(e)}
class PersonaUIManager:
"""Manages UI components for persona selection and interaction"""
def __init__(self):
self.coach_personas = StartupCoachPersonas()
def create_persona_selector(self) -> str:
"""Create HTML interface for persona selection"""
personas = self.coach_personas.get_available_personas()
persona_cards = ""
for persona_id, persona in personas.items():
persona_cards += f"""
<div class="persona-card" data-persona="{persona_id}" style="border-left: 4px solid {persona['color']}">
<div class="persona-header">
<span class="persona-avatar">{persona['avatar']}</span>
<div class="persona-info">
<h3>{persona['name']}</h3>
<p class="persona-background">{persona['background']}</p>
</div>
</div>
<div class="persona-expertise">
<strong>Expertise:</strong>
<div class="expertise-tags">
{' '.join([f'<span class="expertise-tag">{exp}</span>' for exp in persona['expertise']])}
</div>
</div>
<div class="persona-style">
<strong>Style:</strong> {persona['speaking_style']}
</div>
<button class="select-persona-btn" onclick="selectPersona('{persona_id}')">
Choose {persona['name'].split(' ')[0]}
</button>
</div>
"""
return f"""
<div id="persona-selector" class="persona-selector-container">
<div class="selector-header">
<h2>🎯 Choose Your Startup Coach</h2>
<p>Select an AI-powered advisor specialized in your specific needs</p>
</div>
<div class="personas-grid">
{persona_cards}
</div>
<div class="coaching-context">
<h3>Tell us about your startup:</h3>
<div class="context-form">
<input type="text" id="startup-name" placeholder="Startup name" />
<select id="startup-stage">
<option value="">Select stage</option>
<option value="idea">Idea Stage</option>
<option value="mvp">MVP Development</option>
<option value="launch">Pre-Launch</option>
<option value="growth">Growth Stage</option>
<option value="scale">Scaling</option>
</select>
<input type="text" id="industry" placeholder="Industry/Sector" />
<textarea id="challenge" placeholder="Current biggest challenge or question..."></textarea>
</div>
</div>
</div>
<style>
.persona-selector-container {{
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
border-radius: 20px;
padding: 30px;
margin: 20px 0;
font-family: 'Inter', sans-serif;
}}
.selector-header {{
text-align: center;
margin-bottom: 30px;
}}
.selector-header h2 {{
color: #2d3748;
margin-bottom: 10px;
}}
.personas-grid {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}}
.persona-card {{
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
}}
.persona-card:hover {{
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}}
.persona-header {{
display: flex;
align-items: flex-start;
margin-bottom: 15px;
}}
.persona-avatar {{
font-size: 2.5rem;
margin-right: 15px;
}}
.persona-info h3 {{
margin: 0 0 5px 0;
color: #2d3748;
font-size: 1.1rem;
}}
.persona-background {{
color: #718096;
font-size: 0.9rem;
margin: 0;
line-height: 1.4;
}}
.persona-expertise {{
margin-bottom: 15px;
}}
.expertise-tags {{
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 8px;
}}
.expertise-tag {{
background: #e2e8f0;
color: #4a5568;
padding: 4px 8px;
border-radius: 15px;
font-size: 0.8rem;
font-weight: 500;
}}
.persona-style {{
color: #718096;
font-size: 0.9rem;
margin-bottom: 15px;
}}
.select-persona-btn {{
width: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px;
border-radius: 25px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.3s ease;
}}
.select-persona-btn:hover {{
opacity: 0.9;
}}
.coaching-context {{
background: white;
border-radius: 15px;
padding: 25px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}}
.context-form {{
display: grid;
gap: 15px;
margin-top: 15px;
}}
.context-form input,
.context-form select,
.context-form textarea {{
padding: 12px 15px;
border: 2px solid #e2e8f0;
border-radius: 10px;
font-size: 1rem;
transition: border-color 0.3s ease;
}}
.context-form input:focus,
.context-form select:focus,
.context-form textarea:focus {{
outline: none;
border-color: #667eea;
}}
.context-form textarea {{
min-height: 80px;
resize: vertical;
}}
</style>
<script>
function selectPersona(personaId) {{
const startupContext = {{
name: document.getElementById('startup-name').value,
stage: document.getElementById('startup-stage').value,
industry: document.getElementById('industry').value,
challenge: document.getElementById('challenge').value
}};
// Send to backend
if (window.chainlitAPI) {{
window.chainlitAPI.sendMessage({{
type: 'select_persona',
persona_id: personaId,
startup_context: startupContext
}});
}}
// Visual feedback
document.querySelectorAll('.persona-card').forEach(card => {{
card.style.opacity = '0.5';
}});
document.querySelector(`[data-persona="${{personaId}}"]`).style.opacity = '1';
document.querySelector(`[data-persona="${{personaId}}"]`).style.background = '#f0fff4';
}}
</script>
"""