Spaces:
Running
Running
File size: 5,505 Bytes
fdeeb34 c1c5ecb fdeeb34 9e6280a d896c38 fdeeb34 c1c5ecb fdeeb34 9e6280a fdeeb34 c1c5ecb fdeeb34 3858fba fdeeb34 3858fba fdeeb34 e67482e fdeeb34 d896c38 fdeeb34 e67482e a219be1 e67482e fdeeb34 90962fb e67482e fdeeb34 c508545 c1c5ecb e67482e c1c5ecb c508545 c1c5ecb c508545 c1c5ecb e67482e c1c5ecb 9e6280a fdeeb34 08b98ed 9e6280a 7e9d7e2 fdeeb34 9e6280a 08b98ed 9e6280a 08b98ed bd72f20 08b98ed bd72f20 9e6280a 08b98ed bd72f20 9e6280a fdeeb34 9e6280a | 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 | import os
import re
import logging
from typing import List, Dict, Any, Optional
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from pydantic import BaseModel
from dotenv import load_dotenv
from scripts.composer import compose, compose_with_fallback
load_dotenv()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("VeraArchitect")
for key_name in ["NVIDIA_API_KEY", "GROQ_API_KEY", "GEMINI_API_KEY"]:
if not os.getenv(key_name):
logger.warning(f"Missing {key_name} at startup. That tier will be skipped.")
app = FastAPI()
class ContextPayload(BaseModel):
scope: Optional[str] = None
context_id: Optional[str] = None
version: Optional[int] = None
payload: Dict[Any, Any] = {}
class TickRequest(BaseModel):
available_triggers: List[str] = []
contexts = {"category": {}, "merchant": {}, "customer": {}, "trigger": {}}
@app.get("/")
async def root():
return RedirectResponse(url="/docs")
@app.get("/v1/healthz")
async def healthz():
global contexts
contexts = {"category": {}, "merchant": {}, "customer": {}, "trigger": {}}
return {
"status": "ok",
"uptime_seconds": 100,
"contexts_loaded": {
"category": len(contexts["category"]),
"merchant": len(contexts["merchant"]),
"customer": len(contexts["customer"]),
"trigger": len(contexts["trigger"])
}
}
@app.get("/v1/metadata")
async def metadata():
return {
"team_name": "Vera Lead Solver",
"team_members": ["Agent", "User"],
"model": "Fallback Chain (NVIDIA->Groq->Gemini) -> Local Qwen2.5-3B-Instruct",
"approach": "Caveman Distiller + LLM Expander Pipeline",
"contact_email": "rachit.mangawa.ug23@nsut.ac.in",
"version": "1.0",
"submitted_at": "2026-09-26T00:00:00Z"
}
@app.post("/v1/context")
def push_context(data: ContextPayload):
current = contexts.get(data.scope, {}).get(data.context_id)
if current and current.get("version", 0) >= (data.version or 0):
return {"accepted": True, "ack_id": f"ack_{data.context_id}_v{data.version}", "stored_at": "2026-04-26T00:00:00Z", "note": "ignored_stale_version"}
contexts[data.scope][data.context_id] = {"version": data.version, "payload": data.payload}
return {"accepted": True, "ack_id": f"ack_{data.context_id}_v{data.version}", "stored_at": "2026-04-26T00:00:00Z"}
@app.post("/v1/tick")
async def tick(req: TickRequest):
import time
tids = sorted(req.available_triggers, key=lambda t: contexts["trigger"].get(t, {}).get("payload", {}).get("urgency", 0), reverse=True)
request_deadline = time.time() + 27.0
results = []
for tid in tids:
remaining = request_deadline - time.time()
if remaining <= 2.0:
logger.warning(f"Skipping trigger {tid}, {remaining:.1f}s left")
break
res = await process_trigger(tid, deadline=request_deadline)
if res: results.append(res)
return {"actions": results}
async def process_trigger(trigger_id: str, deadline: float = None):
trigger_ctx = contexts["trigger"].get(trigger_id)
if not trigger_ctx: return None
trigger = trigger_ctx["payload"]
merchant = contexts["merchant"].get(trigger.get("merchant_id", ""), {}).get("payload", {})
category = contexts["category"].get(merchant.get("category_slug", ""), {}).get("payload", {"slug": "general"})
customer = contexts["customer"].get(trigger.get("customer_id", ""), {}).get("payload")
composed = await compose(category, merchant, trigger, customer, deadline=deadline)
return {"conversation_id": f"conv_{trigger_id}", "merchant_id": trigger.get("merchant_id"), "trigger_id": trigger_id, "body": composed["body"], "cta": composed["cta"]}
AUTO_REPLY_PATTERNS = [r"thank you for contacting", r"we are currently away", r"automated message", r"business hours", r"auto-reply"]
HOSTILE_PATTERNS = [r"stop", r"spam", r"useless", r"abuse"]
INTENT_PATTERNS = [r"\bok\b", r"\byes\b", r"\bdo it\b", r"let'?s do it", r"\bsure\b"]
@app.post("/v1/reply")
async def reply(req: Dict[str, Any]):
msg, turn = req.get("message", "").lower(), req.get("turn_number", 1)
for p in HOSTILE_PATTERNS:
if re.search(p, msg): return {"action": "end", "rationale": "Hostility."}
for p in AUTO_REPLY_PATTERNS:
if re.search(p, msg):
if turn > 2: return {"action": "end", "rationale": "Repeated auto-reply."}
return {"action": "wait", "wait_seconds": 3600, "rationale": "Auto-reply wait."}
reply_schema = 'RETURN JSON FORMAT WITH keys "body", "cta", and "rationale".'
for p in INTENT_PATTERNS:
if re.search(p, msg):
res = await compose_with_fallback(
f"Merchant said: {msg}",
full_system=f"Role: Vera AI. Interest detected. Provide EXACT next step action. 15 words max. {reply_schema}"
)
return {"action": "send", "body": res.get("body", "I'm setting that up for you now.") if res else "I'm setting that up now.", "cta": "Reply YES"}
res = await compose_with_fallback(
f"Merchant: {msg}",
full_system=f"Role: Vera AI. Growth Strategist. Be concise. {reply_schema}"
)
return {"action": "send", "body": res.get("body", "Understood. Proceed?") if res else "Understood. Proceed?", "cta": "Reply YES"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8080)
|