Spaces:
Running
Running
Rachit-Mangawa commited on
Commit ·
b583ed9
1
Parent(s): c508545
feat: prioritize seasonal beats and trend signals for knowledge-driven engagement
Browse files
app.py
CHANGED
|
@@ -94,9 +94,23 @@ async def compose(category: dict, merchant: dict, trigger: dict, customer: Optio
|
|
| 94 |
lang_pref = ident.get('languages', ['en'])
|
| 95 |
hinglish_note = "USE NATURAL HINGLISH (50% English words, 50% Hindi script/transliteration)." if 'hi' in lang_pref else "USE PROFESSIONAL ENGLISH."
|
| 96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
grounding = f"Metrics: {json.dumps(m)}. Source: {json.dumps(payload)}." if m else "No metrics. Use curiosity hook."
|
| 98 |
sys_prompt = f"""Role: Growth Strategist. Target: {prefix} {owner_name}.
|
| 99 |
-
RULES:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
JSON: {{"body": "...", "cta": "...", "rationale": "..."}}"""
|
| 101 |
|
| 102 |
prompt = f"CONTEXT: {json.dumps({'merchant': ident, 'trigger': trigger})}"
|
|
@@ -149,9 +163,41 @@ async def process_trigger(trigger_id: str):
|
|
| 149 |
"trigger_id": trigger_id, "body": composed["body"], "cta": composed["cta"]
|
| 150 |
}
|
| 151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
@app.post("/v1/reply")
|
| 153 |
async def reply(req: Dict[str, Any]):
|
| 154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
if __name__ == "__main__":
|
| 157 |
import uvicorn
|
|
|
|
| 94 |
lang_pref = ident.get('languages', ['en'])
|
| 95 |
hinglish_note = "USE NATURAL HINGLISH (50% English words, 50% Hindi script/transliteration)." if 'hi' in lang_pref else "USE PROFESSIONAL ENGLISH."
|
| 96 |
|
| 97 |
+
# Knowledge-Driven Prioritization
|
| 98 |
+
beats = category.get("seasonal_beats", [])
|
| 99 |
+
trends = category.get("trend_signals", [])
|
| 100 |
+
knowledge_anchor = ""
|
| 101 |
+
if beats:
|
| 102 |
+
knowledge_anchor = f"PRIORITY BEAT: {json.dumps(beats[0])}. "
|
| 103 |
+
elif trends:
|
| 104 |
+
knowledge_anchor = f"PRIORITY TREND: {json.dumps(trends[0])}. "
|
| 105 |
+
|
| 106 |
grounding = f"Metrics: {json.dumps(m)}. Source: {json.dumps(payload)}." if m else "No metrics. Use curiosity hook."
|
| 107 |
sys_prompt = f"""Role: Growth Strategist. Target: {prefix} {owner_name}.
|
| 108 |
+
RULES:
|
| 109 |
+
1. KNOWLEDGE ANCHOR: {knowledge_anchor}Use this industry insight as the 'Why Now' hook.
|
| 110 |
+
2. NO FABRICATION. {grounding}
|
| 111 |
+
3. CITATION required.
|
| 112 |
+
4. Locality {ident.get('locality')}. {hinglish_note}
|
| 113 |
+
5. 280-310 chars.
|
| 114 |
JSON: {{"body": "...", "cta": "...", "rationale": "..."}}"""
|
| 115 |
|
| 116 |
prompt = f"CONTEXT: {json.dumps({'merchant': ident, 'trigger': trigger})}"
|
|
|
|
| 163 |
"trigger_id": trigger_id, "body": composed["body"], "cta": composed["cta"]
|
| 164 |
}
|
| 165 |
|
| 166 |
+
AUTO_REPLY_PATTERNS = [
|
| 167 |
+
r"thank you for contacting",
|
| 168 |
+
r"we are currently away",
|
| 169 |
+
r"this is an automated message",
|
| 170 |
+
r"business hours",
|
| 171 |
+
r"get back to you shortly",
|
| 172 |
+
r"auto-reply",
|
| 173 |
+
r"canned response"
|
| 174 |
+
]
|
| 175 |
+
|
| 176 |
@app.post("/v1/reply")
|
| 177 |
async def reply(req: Dict[str, Any]):
|
| 178 |
+
body = req.get("body", "").lower()
|
| 179 |
+
|
| 180 |
+
# Fast-Path: Detect Merchant Auto-Reply
|
| 181 |
+
for pattern in AUTO_REPLY_PATTERNS:
|
| 182 |
+
if re.search(pattern, body):
|
| 183 |
+
logger.info(f"Auto-reply detected: {pattern}")
|
| 184 |
+
return {
|
| 185 |
+
"action": "wait",
|
| 186 |
+
"wait_seconds": 3600,
|
| 187 |
+
"rationale": "Merchant auto-reply detected. Waiting for human interaction to avoid loop."
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
# Normal Path: Use call_llm_local for replies
|
| 191 |
+
prompt = f"Merchant said: {req.get('body', '')}. History: {req.get('history', [])}"
|
| 192 |
+
sys = """Role: Vera AI Growth Strategist.
|
| 193 |
+
GOAL: If merchant shows interest in joining, renewing, or acting, provide immediate HIGH-VALUE CTA.
|
| 194 |
+
RULE: Detect 'Intent Threshold'. Do not ask qualifying questions if they said 'YES' or 'I want to join'.
|
| 195 |
+
TONE: Helpful, concise, assertive.
|
| 196 |
+
LIMIT: 100-150 chars."""
|
| 197 |
+
res = await call_llm_local(prompt, system=sys)
|
| 198 |
+
|
| 199 |
+
reply_body = res.get("body", "Understood. Let's move forward. Ready?") if res else "I'm on it. Should we proceed?"
|
| 200 |
+
return {"action": "send", "body": reply_body, "cta": "Reply YES"}
|
| 201 |
|
| 202 |
if __name__ == "__main__":
|
| 203 |
import uvicorn
|