deploy Maps v7 update

This commit is contained in:
Marco0300
2026-09-05 17:17:47 +02:00
parent 4bb71bf842
commit 24b22a3353
5 changed files with 63 additions and 265 deletions
+31 -5
View File
@@ -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