deploy Maps v7 update
This commit is contained in:
+31
-5
@@ -16,7 +16,6 @@ import os
|
||||
import re
|
||||
import sqlite3
|
||||
import threading
|
||||
from threading import Thread
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
@@ -149,6 +148,9 @@ def db() -> sqlite3.Connection:
|
||||
interval_hours INTEGER NOT NULL DEFAULT 24, enabled INTEGER NOT NULL DEFAULT 1,
|
||||
next_run_at TEXT NOT NULL, last_run_id INTEGER, created_at TEXT NOT NULL
|
||||
)""")
|
||||
conn.execute("""CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY, value TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL
|
||||
)""")
|
||||
|
||||
# Migrate databases created by v1-v5 without destroying the user's
|
||||
# catalogue. SQLite permits adding nullable/defaulted columns in place.
|
||||
@@ -891,7 +893,15 @@ def scheduler_loop() -> None:
|
||||
|
||||
def ai_generate_message(prospect: dict[str, Any], offer: str) -> dict[str, Any]:
|
||||
"""Generate an evidence-grounded draft through an OpenAI-compatible endpoint."""
|
||||
if not AI_BASE_URL or not AI_MODEL:
|
||||
runtime = {}
|
||||
try:
|
||||
c = db(); runtime = {str(r["key"]): str(r["value"]) for r in c.execute("SELECT key,value FROM settings")}; c.close()
|
||||
except Exception:
|
||||
pass
|
||||
ai_base_url = runtime.get("AI_BASE_URL", AI_BASE_URL).strip().rstrip("/")
|
||||
ai_api_key = runtime.get("AI_API_KEY", AI_API_KEY).strip()
|
||||
ai_model = runtime.get("AI_MODEL", AI_MODEL).strip()
|
||||
if not ai_base_url or not ai_model:
|
||||
raise RuntimeError("ai_not_configured_set_AI_BASE_URL_and_AI_MODEL")
|
||||
evidence = {
|
||||
"business": prospect.get("name"), "category": prospect.get("category"),
|
||||
@@ -906,8 +916,8 @@ def ai_generate_message(prospect: dict[str, Any], offer: str) -> dict[str, Any]:
|
||||
"Do not mention scraping or hidden analysis. Return strict JSON with keys subject, message, rationale. "
|
||||
"The message must be plain text, personalised, transparent, and include a polite opt-out line.")
|
||||
prompt = json.dumps({"evidence": evidence, "user_offer_or_instruction": offer[:2000]}, ensure_ascii=False)
|
||||
headers = {"Authorization": "Bearer " + AI_API_KEY} if AI_API_KEY else {}
|
||||
result = http_json("POST", AI_BASE_URL + "/chat/completions", {"model": AI_MODEL, "temperature": 0.4, "response_format": {"type": "json_object"}, "messages": [{"role": "system", "content": system}, {"role": "user", "content": prompt}]}, headers=headers, timeout=60)
|
||||
headers = {"Authorization": "Bearer " + ai_api_key} if ai_api_key else {}
|
||||
result = http_json("POST", ai_base_url + "/chat/completions", {"model": ai_model, "temperature": 0.4, "response_format": {"type": "json_object"}, "messages": [{"role": "system", "content": system}, {"role": "user", "content": prompt}]}, headers=headers, timeout=60)
|
||||
content = (((result.get("choices") or [{}])[0].get("message") or {}).get("content") or "") if isinstance(result, dict) else ""
|
||||
try:
|
||||
parsed = json.loads(content)
|
||||
@@ -952,6 +962,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if parsed.path == "/api/config":
|
||||
auth_required = GMAPS_API_MODE == "saas"
|
||||
ready = backend_mode() == "browser" or (bool(GMAPS_URL) and (not auth_required or bool(GMAPS_API_KEY)))
|
||||
saved = {str(r["key"]): str(r["value"]) for r in conn.execute("SELECT key,value FROM settings")}
|
||||
return self.send_json(200, {
|
||||
"backend": backend_mode(),
|
||||
"gmaps_url": bool(GMAPS_URL),
|
||||
@@ -961,8 +972,11 @@ class Handler(BaseHTTPRequestHandler):
|
||||
"ready": ready,
|
||||
"email_extraction_default": True,
|
||||
"email_extraction_always": True,
|
||||
"ai_configured": bool(AI_BASE_URL and AI_MODEL),
|
||||
"ai_configured": bool(saved.get("AI_BASE_URL", AI_BASE_URL) and saved.get("AI_MODEL", AI_MODEL)),
|
||||
})
|
||||
if parsed.path == "/api/settings":
|
||||
saved = {str(r["key"]): str(r["value"]) for r in conn.execute("SELECT key,value FROM settings")}
|
||||
return self.send_json(200, {"maps": {"backend": backend_mode(), "worker_url": GMAPS_URL, "ready": backend_mode() == "browser" or bool(GMAPS_URL)}, "ai": {"base_url": saved.get("AI_BASE_URL", AI_BASE_URL), "model": saved.get("AI_MODEL", AI_MODEL), "configured": bool(saved.get("AI_BASE_URL", AI_BASE_URL) and saved.get("AI_MODEL", AI_MODEL))}, "email_collection": {"always_on": True, "public_only": True}, "limits": {"max_results": MAX_RESULTS, "website_scan_timeout_seconds": 8}})
|
||||
if parsed.path == "/api/prospects":
|
||||
params = parse_qs(parsed.query)
|
||||
q = (params.get("q", [""])[0]).strip().lower()
|
||||
@@ -1041,6 +1055,18 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.wfile.write(content)
|
||||
|
||||
def do_POST(self) -> None:
|
||||
if self.path == "/api/settings":
|
||||
try:
|
||||
payload = self.read_json(); ai = payload.get("ai") if isinstance(payload.get("ai"), dict) else {}
|
||||
conn = db()
|
||||
for key in ("AI_BASE_URL", "AI_API_KEY", "AI_MODEL"):
|
||||
if key in ai:
|
||||
value = str(ai.get(key) or "").strip()[:500]
|
||||
conn.execute("INSERT INTO settings(key,value,updated_at) VALUES(?,?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value,updated_at=excluded.updated_at", (key, value, now()))
|
||||
conn.commit(); saved={str(r["key"]):str(r["value"]) for r in conn.execute("SELECT key,value FROM settings")}; conn.close()
|
||||
return self.send_json(200, {"saved": True, "ai": {"base_url": saved.get("AI_BASE_URL", AI_BASE_URL), "model": saved.get("AI_MODEL", AI_MODEL), "configured": bool(saved.get("AI_BASE_URL", AI_BASE_URL) and saved.get("AI_MODEL", AI_MODEL))}})
|
||||
except Exception as exc:
|
||||
return self.send_json(400, {"error": str(exc)})
|
||||
message_match = re.fullmatch(r"/api/prospects/(\d+)/message", urlparse(self.path).path)
|
||||
if message_match:
|
||||
conn = None
|
||||
|
||||
Reference in New Issue
Block a user