Spaces:
Running
Running
File size: 4,810 Bytes
e561008 889e86b 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 | """
Planning AI Suggestions
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Reads the business profile and generates actionable task / milestone suggestions
grounded in the BFF data and "Built on Rock" philosophy.
Routes:
POST /api/planning/suggest β task or milestone suggestions
"""
import json
from datetime import datetime, timedelta
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 SuggestRequest(BaseModel):
userId: str
businessId: str
type: str # "tasks" | "milestones"
existing: list[str] = [] # existing task/milestone titles (to avoid duplicates)
def _read_context(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", "marketData", "businessModel"]:
doc = biz.collection("profile_output").document(key).get()
if doc.exists:
ctx[key] = doc.to_dict()
s1 = biz.collection("bff").document("section1").get()
if s1.exists:
ctx["section1"] = s1.to_dict()
return ctx
@router.post("/suggest")
def suggest_planning_items(
body: SuggestRequest,
user: dict = Depends(with_rate_limit("generation")),
):
if body.type not in ("tasks", "milestones"):
raise HTTPException(status_code=400, detail="type must be 'tasks' or 'milestones'")
ctx = _read_context(body.userId, body.businessId)
biz_name = ctx.get("businessIdentity", {}).get("businessName", "this business")
sector = ctx.get("businessIdentity", {}).get("sector", "")
value_prop = ctx.get("businessIdentity", {}).get("valueProposition", "")
summary = ctx.get("businessIdentity", {}).get("summary", "")
book_ctx = query_book_context(
f"action plan milestones tasks Nigerian entrepreneur {sector}", top_k=2
)
existing_str = (
f"\nDo NOT duplicate any of these existing items: {body.existing}"
if body.existing else ""
)
system = (
"You are a business consultant helping a Nigerian entrepreneur plan their next steps. "
"Suggest practical, specific, achievable actions grounded in the Business Footing Framework. "
"All suggestions must be relevant to their specific business β no generic advice. "
"Write in plain English. Return ONLY valid JSON."
+ (f"\n\n{book_ctx}" if book_ctx else "")
)
if body.type == "tasks":
prompt = f"""
Business: {biz_name} ({sector})
Value proposition: {value_prop}
Summary: {summary[:400]}
{existing_str}
Generate exactly 5 actionable Kanban tasks for this business right now.
Tasks should be specific, achievable within 2 weeks, and move the business forward.
Spread them across different priorities: some for this week, some for next week.
Return JSON:
{{
"suggestions": [
{{
"title": "specific task title",
"description": "why this matters for {biz_name}",
"column": "todo"
}},
...
]
}}
column must be one of: "todo", "in_progress", "done"
Most tasks should be "todo" unless clearly already started.
"""
else: # milestones
today = datetime.now()
dates = [(today + timedelta(days=d)).strftime("%Y-%m-%d") for d in [30, 60, 90, 180, 365]]
prompt = f"""
Business: {biz_name} ({sector})
Value proposition: {value_prop}
Summary: {summary[:400]}
{existing_str}
Generate exactly 5 business milestones for the next 12 months.
Milestones should be measurable achievements that mark significant progress.
Space them across different timeframes.
Available target dates to choose from: {dates}
(Choose the most appropriate date for each milestone)
Return JSON:
{{
"suggestions": [
{{
"title": "milestone title (specific and measurable)",
"targetDate": "YYYY-MM-DD"
}},
...
]
}}
"""
try:
result = call_groq_json(
system, prompt, model=HEAVY_MODEL, max_tokens=800,
fixture=f"planning_{body.type}",
)
suggestions = result.get("suggestions", [])
if not suggestions:
raise ValueError("No suggestions returned")
return {"suggestions": suggestions}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Suggestion generation failed: {str(e)}")
|