deploy Nous v16 update

This commit is contained in:
Marco0300
2026-09-05 21:19:07 +02:00
parent 53f93d45d2
commit 9da3df08ce
5 changed files with 125 additions and 2 deletions
+99
View File
@@ -1043,6 +1043,82 @@ def ai_generate_message(prospect: dict[str, Any], offer: str) -> dict[str, Any]:
return {"subject": str(parsed["subject"])[:200], "message": str(parsed["message"])[:8000], "rationale": str(parsed.get("rationale") or "")[:2000]}
def _parse_ai_object(content: Any) -> dict[str, Any]:
"""Parse a JSON object from models that occasionally add markdown fences."""
text = str(content or "").strip()
if text.startswith("```"):
text = re.sub(r"^```(?:json)?\s*|\s*```$", "", text, flags=re.I | re.S).strip()
try:
value = json.loads(text)
except (TypeError, ValueError, json.JSONDecodeError):
start, end = text.find("{"), text.rfind("}")
if start < 0 or end <= start:
raise RuntimeError("ai_invalid_json_response")
try:
value = json.loads(text[start:end + 1])
except (TypeError, ValueError, json.JSONDecodeError) as exc:
raise RuntimeError("ai_invalid_json_response") from exc
if not isinstance(value, dict):
raise RuntimeError("ai_invalid_json_response")
return value
def ai_assess_prospect(prospect: dict[str, Any]) -> dict[str, Any]:
"""Assess whether a prospect is worth human review/contact."""
runtime = ai_runtime_settings()
if runtime["provider"] == "nous" and not runtime["api_key"]:
raise RuntimeError("nous_api_key_required_for_standalone_app")
if not runtime["base_url"] or not runtime["model"]:
raise RuntimeError("ai_not_configured_set_AI_BASE_URL_and_AI_MODEL")
evidence = {
"business": prospect.get("name"), "category": prospect.get("category"),
"address": prospect.get("address"), "phone": prospect.get("phone"),
"website": prospect.get("website"), "website_status": prospect.get("website_status"),
"website_http_status": prospect.get("website_http_status"),
"website_title": prospect.get("website_title"), "technology": prospect.get("website_technology"),
"emails": prospect.get("emails"), "rating": prospect.get("rating"),
"review_count": prospect.get("review_count"), "opportunity_score": prospect.get("opportunity_score"),
"score_reasons": _json_list(prospect.get("score_reasons")),
}
system = (
"You qualify prospects for a South African web hosting and web design business. "
"Assess only the supplied public evidence. A high probability means the business appears active "
"and has a plausible hosting, domain, website repair, or redesign need. Do not invent facts, "
"intent, budget, decision-maker names, or contact details. Return strict JSON with exactly these "
"keys: probability (integer 0-100), recommendation (one of contact, review, do_not_contact), "
"reasoning (one concise paragraph)."
)
prompt = json.dumps({"prospect": evidence}, ensure_ascii=False)
headers = {"Authorization": "Bearer " + runtime["api_key"]} if runtime["api_key"] else {}
request_payload: dict[str, Any] = {
"model": runtime["model"], "temperature": 0.1, "max_tokens": 450,
"messages": [{"role": "system", "content": system}, {"role": "user", "content": prompt}],
}
if runtime["provider"] != "nous":
request_payload["response_format"] = {"type": "json_object"}
result = http_json("POST", runtime["base_url"] + "/chat/completions", request_payload, headers=headers, timeout=90, error_prefix="ai")
content = (((result.get("choices") or [{}])[0].get("message") or {}).get("content") or "") if isinstance(result, dict) else ""
parsed = _parse_ai_object(content)
try:
probability = max(0, min(100, int(round(float(parsed.get("probability", 0))))))
except (TypeError, ValueError):
raise RuntimeError("ai_invalid_probability")
recommendation = str(parsed.get("recommendation") or "").strip().lower().replace(" ", "_")
aliases = {
"yes": "contact", "contact_now": "contact", "high": "contact",
"maybe": "review", "needs_review": "review", "medium": "review",
"no": "do_not_contact", "do_not_contact_now": "do_not_contact", "low": "do_not_contact",
}
recommendation = aliases.get(recommendation, recommendation)
if recommendation not in {"contact", "review", "do_not_contact"}:
recommendation = "contact" if probability >= 70 else "review" if probability >= 45 else "do_not_contact"
return {
"ai_probability": probability,
"ai_recommendation": recommendation,
"ai_reasoning": str(parsed.get("reasoning") or "").strip()[:3000],
}
class Handler(BaseHTTPRequestHandler):
server_version = "PersonalProspectOS/2.0"
@@ -1207,6 +1283,29 @@ class Handler(BaseHTTPRequestHandler):
return self.send_json(502, {"error": str(exc)})
except Exception as exc:
return self.send_json(502, {"error": "ai_test_failed:" + str(exc)[:200]})
assess_match = re.fullmatch(r"/api/prospects/(\d+)/assess", urlparse(self.path).path)
if assess_match:
conn = None
try:
conn = db(); prospect_id = int(assess_match.group(1))
row = conn.execute("SELECT * FROM prospects WHERE id=?", (prospect_id,)).fetchone()
if not row:
return self.send_json(404, {"error": "not_found"})
assessment = ai_assess_prospect(dict(row))
stamp = now()
conn.execute(
"UPDATE prospects SET ai_probability=?,ai_recommendation=?,ai_reasoning=?,ai_checked_at=? WHERE id=?",
(assessment["ai_probability"], assessment["ai_recommendation"], assessment["ai_reasoning"], stamp, prospect_id),
)
conn.commit()
return self.send_json(200, {**assessment, "ai_checked_at": stamp, "prospect_id": prospect_id})
except RuntimeError as exc:
return self.send_json(503, {"error": str(exc)})
except (ValueError, TypeError, json.JSONDecodeError) as exc:
return self.send_json(400, {"error": str(exc)})
finally:
if conn is not None:
conn.close()
message_match = re.fullmatch(r"/api/prospects/(\d+)/message", urlparse(self.path).path)
if message_match:
conn = None