system-prompts-and-models-o.../salesflow-saas/backend/app/services/dealix_os/policy_engine.py
Sami Assiri 07557c4be9 feat(dealix): GTM polish, CRM/AI APIs, launch verification hardening
- Add integrations CRM and AI routing APIs; Salesforce OAuth refresh; lead CRM metadata
- Marketer hub, settings CRM UI, OS views; premium landing and strategy_summary differentiators
- Docs: API-MAP, product guide, competitive matrix, launch simulation, AGENT-MAP LLM routing
- Sync script: strategy legal + competitive matrix to public; pytest DB isolation (.pytest_dealix.sqlite)
- Tests: CRM status and AI routing smoke; check_go_live_gate UTF-8 stdout on Windows
- Alembic migrations for strategic deal links and lead company/sector/city

Made-with: Cursor
2026-04-13 05:08:39 +03:00

111 lines
3.7 KiB
Python

"""
Graded policy evaluation: auto_execute | approval_required | blocked.
محرك سياسات متدرج مرتبط بوضع التشغيل والقطاع والقناة والقيمة.
"""
from sqlalchemy.ext.asyncio import AsyncSession
from app.services.strategic_deals.operating_modes import ModeEnforcer, OperatingMode, MODE_POLICIES
from app.services.dealix_os.vertical_playbooks import VERTICAL_PLAYBOOKS
REGULATED_SECTORS = frozenset(
{"healthcare", "pharma", "medical", "finance", "banking", "insurance", "real_estate"}
)
def _normalize_industry(industry: str | None) -> str:
if not industry:
return ""
return industry.strip().lower().replace(" ", "_")
async def evaluate_action(
*,
tenant_id,
channel: str,
action: str,
deal_value_sar: float,
industry: str | None,
db: AsyncSession,
) -> dict:
mode = await ModeEnforcer.get_current_mode(str(tenant_id), db)
policy = MODE_POLICIES.get(mode)
if not policy:
return {
"level": "blocked",
"reason_ar": "وضع تشغيل غير معرّف",
"mode": mode.value,
"mode_name": mode.name,
}
ind = _normalize_industry(industry)
is_regulated = any(s in ind for s in REGULATED_SECTORS) or ind in REGULATED_SECTORS
channel_ok, ch_msg = await ModeEnforcer.check_channel(mode, channel)
if not channel_ok:
return {
"level": "blocked",
"reason_ar": ch_msg,
"mode": mode.value,
"mode_name": mode.name,
"channel": channel,
"action": action,
}
action_ok, act_msg = await ModeEnforcer.check_action(mode, action, deal_value_sar, db)
if not action_ok:
return {
"level": "approval_required",
"reason_ar": act_msg,
"mode": mode.value,
"mode_name": mode.name,
"channel": channel,
"action": action,
}
# Extra gates: regulated + high-touch channels
if is_regulated and channel == "whatsapp" and policy.auto_send:
return {
"level": "approval_required",
"reason_ar": "قطاع حساس: يُفضّل موافقة بشرية قبل إرسال واتساب.",
"mode": mode.value,
"mode_name": mode.name,
"channel": channel,
"action": action,
}
if action in {"send_custom_message", "run_outreach_campaign"} and deal_value_sar > policy.max_auto_commitment_sar > 0:
return {
"level": "approval_required",
"reason_ar": "قيمة الصفقة تتجاوز حد الالتزام التلقائي لوضع التشغيل الحالي.",
"mode": mode.value,
"mode_name": mode.name,
"channel": channel,
"action": action,
}
return {
"level": "auto_execute",
"reason_ar": "مسموح ضمن السياسات الحالية.",
"mode": mode.value,
"mode_name": mode.name,
"channel": channel,
"action": action,
}
def suggested_playbook_for_industry(industry: str | None) -> str | None:
"""Pick a default vertical playbook id from coarse industry string."""
ind = _normalize_industry(industry)
if not ind:
return None
if any(x in ind for x in ("عقار", "real", "estate", "property")):
return "real_estate"
if any(x in ind for x in ("صح", "health", "medical", "clinic", "hospital")):
return "healthcare"
if any(x in ind for x in ("saas", "software", "tech", "برمج")):
return "saas_b2b"
if any(x in ind for x in ("consult", "legal", "محاسب", "service")):
return "professional_services"
return None