""" Document Content Generation Route ────────────────────────────────────────────────────────────────────────────── Generates structured content for Business Plan and Pitch Deck using Groq with full key rotation. The generated content is saved to Firestore and returned to the frontend for client-side PDF rendering via jsPDF. Profile-based documents (BMC, Identity, Customer, Market) do NOT use this endpoint — they generate PDF directly from profile_output in the frontend. This endpoint handles only the documents that require AI narrative generation: - business_plan → 9-section written plan - pitch_deck → 7-slide pitch outline """ import json 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 from utils import business_type router = APIRouter() class GenerateDocRequest(BaseModel): userId: str businessId: str docType: str # "business_plan" | "pitch_deck" def _get_bff_and_profile(user_id: str, business_id: str) -> dict: """Reads BFF sections + profile_output from Firestore into one context dict.""" db = firestore.client() biz = ( db.collection("user_data") .document(user_id) .collection("businesses") .document(business_id) ) context: dict = {} # BFF sections for section in ["section1", "section2", "section3"]: doc = biz.collection("bff").document(section).get() if doc.exists: context[section] = doc.to_dict() # Profile outputs for key in ["businessIdentity", "customerProfile", "marketData", "businessModel"]: doc = biz.collection("profile_output").document(key).get() if doc.exists: context[key] = doc.to_dict() return context # ─── Business Plan ──────────────────────────────────────────────────────────── def _generate_business_plan(ctx: dict, book_context: str) -> dict: system = ( "You are a professional business consultant specializing in Nigerian MSMEs. " "Write a complete, practical Business Plan for this entrepreneur. " "Use plain English — no jargon. Every section must be actionable and specific " "to the Nigerian market. Return ONLY valid JSON.\n\n" + (book_context or "") ) prompt = f""" Business context: {json.dumps(ctx, indent=2)[:3000]} Generate a comprehensive Business Plan. Return JSON: {{ "executiveSummary": "2-3 paragraphs summarizing the business, opportunity, and goals", "businessOpportunity": "The problem being solved and why now", "companyDescription": "What the business does, its mission, and legal structure", "targetMarket": "Detailed description of the target market in Nigeria", "productsServices": "What is being sold, key features, pricing approach", "marketingStrategy": "How the business will reach and retain customers", "operationsPlan": "Day-to-day operations, location, equipment, team", "financialSummary": "Startup costs, expected revenue, break-even estimate", "implementationTimeline": "Key milestones for the first 12 months" }}""" return call_groq_json(system, prompt, model=HEAVY_MODEL, max_tokens=2500, fixture="business_plan") # ─── Pitch Deck ──────────────────────────────────────────────────────────────── def _generate_pitch_deck(ctx: dict, book_context: str) -> dict: system = ( "You are a venture capital pitch consultant. " "Create a compelling 7-slide investor pitch deck for this Nigerian entrepreneur. " "Be concise — each slide has 3-4 punchy bullet points. " "Ground everything in real Nigerian market data where possible. " "Return ONLY valid JSON.\n\n" + (book_context or "") ) prompt = f""" Business context: {json.dumps(ctx, indent=2)[:3000]} Generate a 7-slide Pitch Deck. Return JSON where each key is a slide: {{ "slide1_problem": {{ "title": "The Problem", "bullets": ["...", "...", "..."] }}, "slide2_solution": {{ "title": "Our Solution", "bullets": ["...", "...", "..."] }}, "slide3_market": {{ "title": "Market Opportunity", "bullets": ["TAM...", "SAM...", "SOM..."] }}, "slide4_business_model": {{ "title": "How We Make Money", "bullets": ["...", "...", "..."] }}, "slide5_traction": {{ "title": "Traction & Milestones", "bullets": ["...", "...", "..."] }}, "slide6_strategy": {{ "title": "Go-To-Market Strategy", "bullets": ["...", "...", "..."] }}, "slide7_ask": {{ "title": "The Ask", "bullets": ["Seeking: ...", "Use of funds: ...", "Expected outcome: ..."] }} }}""" return call_groq_json(system, prompt, model=HEAVY_MODEL, max_tokens=2000, fixture="pitch_deck") # ─── Route ──────────────────────────────────────────────────────────────────── @router.post("/generate-content") def generate_document_content( body: GenerateDocRequest, user: dict = Depends(with_rate_limit("generation")), ): """ Generates AI content for Business Plan or Pitch Deck. Saves the result to Firestore and returns the document ID. PDF rendering happens client-side in the browser. """ if user.get("uid") != body.userId: raise HTTPException(status_code=403, detail="Forbidden.") if body.docType not in ("business_plan", "pitch_deck"): raise HTTPException( status_code=400, detail="docType must be 'business_plan' or 'pitch_deck'", ) # ── Read context ───────────────────────────────────────────────────────── ctx = _get_bff_and_profile(body.userId, body.businessId) if not ctx: raise HTTPException(status_code=404, detail="Business profile not found.") # ── BFF Philosophy context from Pinecone ────────────────────────────────── t = business_type.describe(ctx.get("section1", {})) query = f"business plan entrepreneurship Nigeria {t['subsector']} {t['niche']}" book_context = query_book_context(query, top_k=3) # ── Generate ────────────────────────────────────────────────────────────── try: if body.docType == "business_plan": content = _generate_business_plan(ctx, book_context) doc_name = f"{ctx.get('businessIdentity', {}).get('businessName', 'Business')}_Plan" else: content = _generate_pitch_deck(ctx, book_context) doc_name = f"{ctx.get('businessIdentity', {}).get('businessName', 'Business')}_PitchDeck" except Exception as e: raise HTTPException(status_code=500, detail=f"Generation failed: {str(e)}") # ── Save to Firestore ───────────────────────────────────────────────────── from google.cloud.firestore_v1 import SERVER_TIMESTAMP db = firestore.client() biz = ( db.collection("user_data") .document(body.userId) .collection("businesses") .document(body.businessId) ) doc_ref = biz.collection("documents").add({ "name": doc_name, "type": body.docType, "dataSnapshot": content, "createdAt": SERVER_TIMESTAMP, "status": "ready", }) doc_id = doc_ref[1].id return { "success": True, "docId": doc_id, "dataSnapshot": content, }