Add Matrix intake bot (M1+M2): typed message → approved fundraising-grid write
New backend/matrix_intake/ runs as its own process (matrix-nio isolated from the stdlib CRM): local-Qwen parse via Spark Control → in-thread human approval (yes/edit/no) → write through the CRM's own log-communication endpoint, tagged source=matrix_intake. Adds read-only GET /api/intake/match (returns grid row id, no-duplicate contract); threads provenance through handle_log_fundraising_communication. Reviewer-passed: pop-before-commit closes a double-approve race; edit-grammar fix. Text-only v1; business-card photo (M3) deferred (no Spark vision model). 26/26 tests green; live Matrix smoke pending deploy.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
"""Thin reuse of the in-repo local-Qwen client (backend/ingest/llm.py) via Spark Control.
|
||||
|
||||
We import the ingest client rather than re-implementing the HTTP call so the intake bot
|
||||
speaks the exact same Spark contract (model, /v1/chat/completions, TLS verify, .env load).
|
||||
The intake message is real LP substance, but it goes ONLY to the local Qwen on Ten31 infra
|
||||
— never Claude — so no scrub boundary applies (same basis as the daily digest). Never call a
|
||||
Spark directly; everything goes through SPARK_CONTROL_URL.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
_INGEST = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "ingest")
|
||||
if _INGEST not in sys.path:
|
||||
sys.path.insert(0, _INGEST)
|
||||
|
||||
import llm # noqa: E402 (backend/ingest/llm.py — chat / chat_json over Spark Control)
|
||||
|
||||
|
||||
def parse_json(prompt, system=None, max_tokens=400):
|
||||
"""Send to local Qwen (temp 0, thinking off) and parse the first JSON object, or None."""
|
||||
return llm.chat_json(prompt, system=system, max_tokens=max_tokens)
|
||||
Reference in New Issue
Block a user