Spaces:
Running
Running
Download routes/documents.py from Success-dev/venthrive-api: direct link, hf CLI and curl.
- Browser
- Download file 8.43 kB
-
https://huggingface.co/spaces/Success-dev/venthrive-api/resolve/main/routes/documents.py
- Command line
-
hf download hf://spaces/Success-dev/venthrive-api/routes/documents.py
-
curl -L -o documents.py https://huggingface.co/spaces/Success-dev/venthrive-api/resolve/main/routes/documents.py
8.43 kB
| """ | |
| 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 ──────────────────────────────────────────────────────────────────── | |
| 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, | |
| } | |