Spaces:
Running
Running
File size: 6,717 Bytes
e561008 889e86b e561008 889e86b e561008 020705e e561008 889e86b e561008 020705e e561008 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | """
AI Consultation Session Routes
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Manages proactive AI Consultation sessions distinct from passive AI Chat.
AI Chat: passive β waits for user questions
Consultation: proactive β AI opens with agenda, leads through structured phases
(Discovery β Analysis β Prescription) and produces a written report
Routes:
POST /api/consultation/start β AI reads profile, generates opening message
POST /api/consultation/report β AI generates structured report from session
"""
import json
from datetime import datetime
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from firebase_admin import firestore
from utils.rate_limiter import with_rate_limit
from utils.groq_client import call_groq_json, HEAVY_MODEL
from utils.pinecone_client import query_book_context
router = APIRouter()
class StartRequest(BaseModel):
userId: str
businessId: str
class ReportRequest(BaseModel):
userId: str
businessId: str
messages: list[dict] # [{"role": "user"|"assistant", "content": "..."}]
def _read_profile(user_id: str, business_id: str) -> dict:
db = firestore.client()
biz = (
db.collection("user_data").document(user_id)
.collection("businesses").document(business_id)
)
ctx: dict = {}
for key in ["businessIdentity", "customerProfile", "marketData"]:
doc = biz.collection("profile_output").document(key).get()
if doc.exists:
ctx[key] = doc.to_dict()
return ctx
@router.post("/start")
def start_consultation(
body: StartRequest,
user: dict = Depends(with_rate_limit("generation")),
):
"""
Reads the entrepreneur's business profile and generates a warm, structured
opening message with 5 focus areas. The AI has already 'read the brief'
so the session starts with context rather than from scratch.
"""
profile = _read_profile(body.userId, body.businessId)
identity = profile.get("businessIdentity", {})
biz_name = identity.get("businessName", "your business")
sector = identity.get("sector", "")
value_prop = identity.get("valueProposition", "")
summary = identity.get("summary", "")
book_ctx = query_book_context(
"business consultation strategy discovery analysis Nigerian entrepreneur",
top_k=2,
)
system = (
"You are a senior business consultant at VenThrive, trained in the "
"Business Footing Framework from 'Built on Rock' by Iyanuoluwa Joseph Akinnusi. "
"You are warm, direct, and speak in plain everyday English. "
"You do NOT use business jargon. You speak like a trusted advisor, not a textbook. "
"Return ONLY valid JSON."
+ (f"\n\n{book_ctx}" if book_ctx else "")
)
prompt = f"""
You are starting a consultation session with the founder of "{biz_name}"
({sector}). Their value proposition is: "{value_prop}".
Brief summary: {summary[:300]}
Generate a warm, professional opening message that:
1. Greets them by referencing their specific business (not generic)
2. Mentions you've reviewed their Business Footing Framework profile
3. Presents exactly 5 focus areas as a numbered list
4. Asks them to pick the one they want to focus on today
The 5 focus areas should be:
1. Strategy & Market Position
2. Customer Acquisition & Retention
3. Financial Planning & Pricing
4. Operations & Standard Processes
5. Investor Readiness & Growth
Return JSON:
{{
"openingMessage": "<your full opening message here>",
"focusAreas": ["Strategy & Market Position", "Customer Acquisition & Retention", "Financial Planning & Pricing", "Operations & Standard Processes", "Investor Readiness & Growth"]
}}"""
try:
result = call_groq_json(system, prompt, model=HEAVY_MODEL, max_tokens=800, fixture="consultation_start")
return {
"openingMessage": result.get("openingMessage", ""),
"focusAreas": result.get("focusAreas", []),
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Could not start consultation: {str(e)}")
@router.post("/report")
def generate_report(
body: ReportRequest,
user: dict = Depends(with_rate_limit("generation")),
):
"""
Takes the full session transcript and generates a structured consultation
report with findings, specific recommendations, and next steps.
"""
if user.get("uid") != body.userId:
raise HTTPException(status_code=403, detail="Forbidden.")
if len(body.messages) < 2:
raise HTTPException(status_code=400, detail="Session too short to generate a report.")
transcript = "\n".join([
f"{'CONSULTANT' if m['role'] == 'assistant' else 'ENTREPRENEUR'}: {m['content']}"
for m in body.messages
])
book_ctx = query_book_context(
"consultation findings recommendations action plan Nigerian business",
top_k=2,
)
system = (
"You are a business consultant writing a formal post-consultation report. "
"Analyze the conversation and produce a structured, actionable document. "
"Be specific to what was actually discussed β no generic advice. "
"Write in clear, plain English. "
"Return ONLY valid JSON."
+ (f"\n\n{book_ctx}" if book_ctx else "")
)
prompt = f"""
Consultation transcript:
{transcript[:4000]}
Today's date: {datetime.now().strftime('%B %d, %Y')}
Write a formal consultation report. Return JSON:
{{
"date": "{datetime.now().strftime('%B %d, %Y')}",
"sessionSummary": "2-3 sentences summarising what was discussed and the main concern addressed",
"keyFindings": [
"Specific finding 1 from the conversation",
"Specific finding 2",
"Specific finding 3"
],
"recommendations": [
{{
"action": "What to do",
"rationale": "Why this matters for their specific business",
"timeline": "When to do it (e.g. This week / Within 30 days)"
}}
],
"nextSteps": [
"Concrete action step 1",
"Concrete action step 2",
"Concrete action step 3"
],
"closingNote": "A personalised, encouraging closing sentence referencing their business."
}}"""
try:
result = call_groq_json(system, prompt, model=HEAVY_MODEL, max_tokens=2000, fixture="consultation_report")
return {"report": result}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Report generation failed: {str(e)}")
|