Spaces:
Sleeping
Sleeping
File size: 6,921 Bytes
fdeeb34 19836c6 | 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 188 189 | # BEST SCORE: 41/50 (82%) - EXCELLENT
# Model: Gemini-2.0-Flash-001 (via OpenRouter)
# Tested on: 2026-05-02
# Features: Strict grounding, Trigger primacy, Hinglish support, Source citations.
import os
import json
import logging
import asyncio
import time
import re
from typing import List, Dict, Any, Optional
from fastapi import FastAPI
from pydantic import BaseModel
import httpx
from dotenv import load_dotenv
load_dotenv()
# Lead AI Problem Solver: Sequential Reinforcement Architecture
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("VeraLeadSolver")
app = FastAPI()
class ContextPayload(BaseModel):
scope: str
context_id: str
version: int
payload: Dict[Any, Any]
class TickRequest(BaseModel):
available_triggers: List[str]
# Distributed Credential Strategy: Round-Robin Key Pool
OPENROUTER_KEYS = [
os.getenv("OPENROUTER_KEY_1"),
os.getenv("OPENROUTER_KEY_2"),
os.getenv("OPENROUTER_KEY_3"),
os.getenv("OPENROUTER_KEY_4"),
os.getenv("OPENROUTER_KEY_5"),
os.getenv("OPENROUTER_KEY_6")
]
OPENROUTER_KEYS = [k for k in OPENROUTER_KEYS if k]
class KeyRotator:
def __init__(self, keys: List[str]):
self.keys = keys
self.index = 0
self.lock = asyncio.Lock()
async def get_key(self) -> str:
if not self.keys:
return ""
async with self.lock:
key = self.keys[self.index]
self.index = (self.index + 1) % len(self.keys)
return key
or_rotator = KeyRotator(OPENROUTER_KEYS)
contexts = {"category": {}, "merchant": {}, "customer": {}, "trigger": {}}
semaphore = asyncio.Semaphore(1) # STRICT SEQUENTIAL
async def call_llm(prompt: str, system: str = "", temperature: float = 0.0) -> Optional[Dict]:
key = await or_rotator.get_key()
url = "https://openrouter.ai/api/v1/chat/completions"
headers = {
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
"HTTP-Referer": "https://vera.ai",
"X-Title": "Vera Lead Solver"
}
# Using openrouter/free to get the best free model
payload = {
"model": "openrouter/free",
"messages": [
{"role": "system", "content": system + "\n\nCRITICAL: Return ONLY valid JSON. No markdown, no preambles."},
{"role": "user", "content": prompt}
],
"temperature": temperature,
"max_tokens": 1000
}
async with httpx.AsyncClient() as client:
try:
response = await client.post(url, headers=headers, json=payload, timeout=60.0)
if response.status_code == 200:
resp_json = response.json()
content = resp_json["choices"][0]["message"].get("content")
if not content:
return None
content = re.sub(r'```json\s*|\s*```', '', content).strip()
start = content.find('{')
end = content.rfind('}')
if start != -1 and end != -1:
content = content[start:end+1]
return json.loads(content)
else:
logger.error(f"LLM Error {response.status_code}: {response.text}")
return None
except Exception as e:
logger.error(f"LLM Exception: {e}")
return None
async def compose(category: dict, merchant: dict, trigger: dict, customer: Optional[dict] = None) -> dict:
ident = merchant.get("identity", {})
perf = merchant.get("performance", {})
owner_name = ident.get("owner_first_name", "Partner")
sys_prompt = f"""Role: Elite Growth Strategist for magicpin.
Objective: Compose a high-impact WhatsApp message.
STRICT GROUNDING RULES:
1. NO FABRICATION: Only use numbers present in context.
2. TRIGGER PRIMACY: Message MUST address the trigger event first.
3. SPECIFICITY: Use metrics like views, calls, directions, or CTR from Context.
4. CATEGORY FIT: For dentists, use "Dr." and clinical peer tone.
5. LANGUAGE: If 'hi' in languages, use Hinglish.
6. SOURCE: Include source citations (e.g. "JIDA Oct") if present.
Output JSON: {{"body": "...", "cta": "...", "rationale": "..."}}
"""
context_str = f"CATEGORY: {json.dumps(category)}\nMERCHANT: {json.dumps(merchant)}\nTRIGGER: {json.dumps(trigger)}\nCUSTOMER: {json.dumps(customer) if customer else 'None'}"
res = await call_llm(f"Context: {context_str}\n\nCompose a grounded message based on trigger.", system=sys_prompt)
if not res:
v = perf.get('views', '?')
c = perf.get('calls', '?')
return {"body": f"Hi {owner_name}, noticed {v} views and {c} calls on your profile. Important {trigger.get('kind')} update inside. Check?", "cta": "Reply YES", "rationale": "Fallback"}
return res
@app.get("/v1/healthz")
def healthz():
return {"status": "ok", "contexts_loaded": {s: len(c) for s, c in contexts.items()}}
@app.get("/v1/metadata")
def metadata():
return {"team_name": "Vera Lead Solver", "model": "OpenRouter-Free", "version": "26.0.0"}
@app.post("/v1/context")
def push_context(data: ContextPayload):
contexts[data.scope][data.context_id] = data.payload
return {"accepted": True}
async def process_trigger(trigger_id: str):
async with semaphore:
trigger = contexts["trigger"].get(trigger_id, {})
merchant_id = trigger.get("merchant_id")
merchant = contexts["merchant"].get(merchant_id, {})
category_slug = merchant.get("category_slug", "general")
category = contexts["category"].get(category_slug, {"slug": category_slug})
customer_id = trigger.get("customer_id")
customer = contexts["customer"].get(customer_id, {}) if customer_id else None
composed_data = await compose(category, merchant, trigger, customer)
is_customer_facing = trigger.get("scope") == "customer"
return {
"conversation_id": f"conv_{trigger_id}",
"merchant_id": merchant_id,
"customer_id": customer_id if is_customer_facing else None,
"send_as": "merchant_on_behalf" if is_customer_facing else "vera",
"trigger_id": trigger_id,
"template_name": "vera_v26",
"body": composed_data["body"],
"cta": composed_data["cta"],
"suppression_key": trigger_id,
"rationale": composed_data.get("rationale", "Free Router composition")
}
@app.post("/v1/tick")
async def tick(req: TickRequest):
results = []
for tid in req.available_triggers:
res = await process_trigger(tid)
if res: results.append(res)
return {"actions": results}
@app.post("/v1/reply")
async def reply(req: Dict[str, Any]):
return {"action": "send", "body": "Got it. Let me set that up for you.", "cta": "Reply YES"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8080)
|