fix: preserve Chatwoot message visibility and identity

This commit is contained in:
plx
2026-08-14 14:08:23 +00:00
parent 475ff15b9b
commit c4d55d37ed
10 changed files with 531 additions and 71 deletions

View File

@@ -4,13 +4,32 @@ from app.action_decider import decide_action
from app.action_mapper import map_action_decision
from app.config import settings
from app.persistence import save_action_run
from app.persistence import save_inbound_message
from app.schemas import ActionDecision, AnalyzeRequest, AnalyzeResponse, UsageInfo
from app.task_service import create_task_from_action_result
async def analyze(request: AnalyzeRequest, raw_event_id: str | None = None) -> AnalyzeResponse:
async def analyze(
request: AnalyzeRequest,
raw_event_id: str | None = None,
source_event_id: str | None = None,
) -> AnalyzeResponse:
needs_review = False
# The inbound fact is durable before any LLM/policy decision. This keeps
# ignored classifications and decision failures visible and replay-safe.
message_id, source_event_id = save_inbound_message(
request=request, raw_event_id=raw_event_id, source_event_id=source_event_id,
raw_body=request.last_customer_message, clean_body=request.last_customer_message,
)
if (request.source or "").lower() == "chatwoot" and source_event_id:
from app.communication_service import upsert_chatwoot_inbound_communication
upsert_chatwoot_inbound_communication(
source_message_id=source_event_id, conversation_id=request.conversation_id,
contact_id=request.contact_id, body=request.last_customer_message,
metadata={"raw_event_id": raw_event_id, "message_id": message_id},
)
try:
decision, action_result, usage, decision_source = await decide_action(request)
except Exception as exc:
@@ -32,24 +51,11 @@ async def analyze(request: AnalyzeRequest, raw_event_id: str | None = None) -> A
)
decision_source = "fallback"
if str(action_result.action_code or "").upper() == "IGNORE_BOUNCE":
# v4.8.5: NDR/bounce emails are already readable in Chatwoot/Thunderbird.
# They must not create ClientFlow work, opportunities or fiscal customers.
return AnalyzeResponse(
app="ClientFlow",
model=settings.openrouter_model,
action_decision=decision,
action_result=action_result,
usage=usage,
needs_review=False,
action_run_id=None,
message_id=None,
task_id=None,
)
if not action_result.safe_to_post:
needs_review = True
is_ignored_bounce = str(action_result.action_code or "").upper() == "IGNORE_BOUNCE"
action_run_id, message_id = save_action_run(
request=request,
action_decision=decision,
@@ -61,6 +67,8 @@ async def analyze(request: AnalyzeRequest, raw_event_id: str | None = None) -> A
raw_body=request.last_customer_message,
clean_body=request.last_customer_message,
raw_event_id=raw_event_id,
source_event_id=source_event_id,
message_id=message_id,
)
normalized_message_for_idem = re.sub(r"\s+", " ", str(request.last_customer_message or "").strip().casefold())
@@ -74,7 +82,7 @@ async def analyze(request: AnalyzeRequest, raw_event_id: str | None = None) -> A
conversation_id=request.conversation_id,
contact_id=request.contact_id,
source_system=request.source or "manual",
source_event_id=message_id,
source_event_id=source_event_id or message_id,
metadata={
"created_from_analyzer": True,
"content_fingerprint": message_fingerprint,
@@ -89,6 +97,19 @@ async def analyze(request: AnalyzeRequest, raw_event_id: str | None = None) -> A
},
)
if (request.source or "").lower() == "chatwoot" and source_event_id:
from app.communication_service import enrich_chatwoot_communication_from_task
action_code = str(action_result.action_code or "").upper()
enrich_chatwoot_communication_from_task(
source_message_id=source_event_id, classification=action_code,
confidence=float(getattr(decision, "confidence", 0.0) or 0.0),
ignored=is_ignored_bounce or action_code in {"IGNORE_SPAM", "SPAM"},
conversation_id=request.conversation_id, contact_id=request.contact_id,
body=request.last_customer_message, task_id=task_id,
metadata={"raw_event_id": raw_event_id, "message_id": message_id,
"action_run_id": action_run_id, "decision_source": decision_source},
)
return AnalyzeResponse(
app="ClientFlow",
model=settings.openrouter_model,