system-prompts-and-models-o.../dealix/api/routers/intelligence_layer.py
Dealix Builder 4e969131c7 feat(platform+intelligence): Growth Control Tower + Growth Neural Network — 20 modules + 25 endpoints + 60 tests
Platform Services Layer (10 modules) — برج التحكم بالنمو
- event_bus: 27 typed events (whatsapp/email/calendar/lead/payment/review/social/partner/sheet/crm/action)
- identity_resolution: cross-channel merge (phone+email+CRM+social) with confidence scoring
- channel_registry: 11 channels (WA, Gmail, Calendar, Moyasar, LinkedIn, X, IG, GBP, Sheets, CRM, Forms) with capabilities/risk/PDPL notes
- action_policy: 9 rules (block_cold_whatsapp, block_payment_no_confirm, block_secrets, external_send_needs_approval, calendar_insert_needs_approval, social_dm_needs_explicit, unknown_source_review, high_value_deal_review, draft_only_safe)
- tool_gateway: single execution chokepoint, env-flag-gated live actions (default OFF)
- unified_inbox: 8 card types, ≤3 buttons enforced, Arabic
- action_ledger: requested→approved→executed audit trail
- proof_ledger: leads/meetings/drafts/sends/payments/revenue/risks_blocked/time_saved per channel
- service_catalog: 12 sellable services
- router api/routers/platform_services.py — 13 endpoints under /api/v1/platform/

Intelligence Layer (10 modules) — الشبكة العصبية للنمو
- growth_brain: per-customer Brain + is_ready_for_autopilot() (≥30 signals + ≥40% accept)
- command_feed: 9 daily card types (opportunity/revenue_leak/partner_suggestion/meeting_prep/review_response/competitive_move/customer_reactivation/ai_visibility_alert/action_required)
- action_graph: 10 typed edges (signal→action→outcome) with what_works_summary
- mission_engine: 7 missions, KILL FEATURE first_10_opportunities (10 فرص في 10 دقائق)
- decision_memory: learns from accept/skip/edit/block, returns preferences (channels, tones, sectors, rejected actions, accept_rate)
- trust_score: composite 0-100 (source+opt_in+channel+content+freq+approval) → safe/needs_review/blocked
- revenue_dna: best_channel/segment/angle + common_objection + avg_cycle_days
- opportunity_simulator: 9 Saudi sectors, expected_replies/meetings/deals/pipeline_sar + risk_score
- competitive_moves: 8 move types with Arabic recommended_action_ar
- board_brief: weekly Founder Shadow Board (3 decisions + 3 opportunities + 3 risks + relationship + experiment + metric)
- router api/routers/intelligence_layer.py — 12 endpoints under /api/v1/intelligence/

Tests
- tests/unit/test_platform_services.py — 31 tests covering catalog/channels/events/policy/gateway/identity/inbox/ledger/proof
- tests/unit/test_intelligence_layer.py — 29 tests covering brain/feed/graph/missions/memory/trust/dna/simulator/competitive/brief
- 60/60 new tests pass; full suite 587 passed, 2 skipped

Docs
- docs/PLATFORM_SERVICES_STRATEGY.md (Arabic)
- docs/INTELLIGENCE_LAYER_STRATEGY.md (Arabic)
- docs/DEALIX_100_PERCENT_LAUNCH_PLAN.md — added §32 Platform Services + §33 Intelligence Layer

Safety
- No live send by default (all WA/Gmail/Calendar/Moyasar guarded by env flags, all OFF)
- All external actions go through Tool Gateway → Action Policy → draft/approval_required
- No secrets allowed in payloads (block_secrets policy)
- PDPL-aware: cold WhatsApp without consent is hard-blocked
- Existing 477+ tests untouched (no breaking changes)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:05:12 +03:00

141 lines
5.9 KiB
Python

"""Intelligence Layer router — growth brain + missions + DNA + simulator + brief."""
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Body
from auto_client_acquisition.intelligence_layer import (
DecisionMemory,
analyze_competitive_move,
build_board_brief,
build_command_feed_demo,
build_growth_brain,
build_revenue_dna_demo,
compute_trust_score,
extract_revenue_dna,
learn_from_decision,
list_intel_missions,
recommend_missions,
simulate_opportunity,
)
router = APIRouter(prefix="/api/v1/intelligence", tags=["intelligence-layer"])
# Per-customer in-memory decision memory (demo; production = Supabase)
_MEMORY: dict[str, DecisionMemory] = {}
def _memory_for(customer_id: str) -> DecisionMemory:
if customer_id not in _MEMORY:
_MEMORY[customer_id] = DecisionMemory(customer_id=customer_id)
return _MEMORY[customer_id]
# ── Growth Brain ──────────────────────────────────────────────
@router.post("/growth-brain/build")
async def growth_brain_build(payload: dict[str, Any] = Body(default_factory=dict)) -> dict[str, Any]:
brain = build_growth_brain(payload)
return {**brain.to_dict(), "ready_for_autopilot": brain.is_ready_for_autopilot()}
# ── Command Feed ──────────────────────────────────────────────
@router.get("/command-feed/demo")
async def command_feed_demo() -> dict[str, Any]:
return build_command_feed_demo()
# ── Missions ──────────────────────────────────────────────────
@router.get("/missions")
async def missions_list() -> dict[str, Any]:
return list_intel_missions()
@router.post("/missions/recommend")
async def missions_recommend(payload: dict[str, Any] = Body(default_factory=dict)) -> dict[str, Any]:
brain_payload = payload.get("growth_brain") or payload
brain = build_growth_brain(brain_payload) if brain_payload else None
return recommend_missions(brain, limit=int(payload.get("limit", 3)))
# ── Trust Score ───────────────────────────────────────────────
@router.post("/trust-score")
async def trust_score(payload: dict[str, Any] = Body(...)) -> dict[str, Any]:
return compute_trust_score(
source_quality=payload.get("source_quality", "unknown"),
opt_in=bool(payload.get("opt_in", False)),
channel=payload.get("channel", "whatsapp"),
message_text=payload.get("message_text", ""),
frequency_count_this_week=int(payload.get("frequency_count_this_week", 0)),
weekly_cap=int(payload.get("weekly_cap", 2)),
approval_status=payload.get("approval_status", "pending"),
)
# ── Revenue DNA ───────────────────────────────────────────────
@router.get("/revenue-dna/demo")
async def revenue_dna_demo() -> dict[str, Any]:
return build_revenue_dna_demo()
@router.post("/revenue-dna")
async def revenue_dna_post(payload: dict[str, Any] = Body(...)) -> dict[str, Any]:
return extract_revenue_dna(
customer_id=payload.get("customer_id", "unknown"),
won_deals=payload.get("won_deals", []),
replies=payload.get("replies", []),
objections=payload.get("objections", []),
)
# ── Opportunity Simulator ─────────────────────────────────────
@router.post("/simulate-opportunity")
async def simulate_opportunity_endpoint(payload: dict[str, Any] = Body(...)) -> dict[str, Any]:
return simulate_opportunity(
target_count=int(payload.get("target_count", 100)),
sector=payload.get("sector", "saas"),
avg_deal_value_sar=float(payload.get("avg_deal_value_sar", 25_000)),
channel=payload.get("channel", "whatsapp"),
cold_pct=float(payload.get("cold_pct", 0)),
quality_lift=float(payload.get("quality_lift", 1.0)),
)
# ── Competitive Moves ─────────────────────────────────────────
@router.post("/competitive-move/analyze")
async def competitive_move_analyze(payload: dict[str, Any] = Body(...)) -> dict[str, Any]:
return analyze_competitive_move(
competitor_name=payload.get("competitor_name", "?"),
move_type=payload.get("move_type", "new_offer"),
payload=payload.get("payload", {}),
)
# ── Board Brief ───────────────────────────────────────────────
@router.get("/board-brief/demo")
async def board_brief_demo() -> dict[str, Any]:
return build_board_brief()
# ── Decision Memory ───────────────────────────────────────────
@router.post("/decisions/record")
async def decisions_record(payload: dict[str, Any] = Body(...)) -> dict[str, Any]:
customer_id = payload.get("customer_id", "demo")
mem = _memory_for(customer_id)
return learn_from_decision(
memory=mem,
decision=payload.get("decision", "skip"),
action_type=payload.get("action_type", "send_whatsapp"),
channel=payload.get("channel", "whatsapp"),
sector=payload.get("sector"),
tone=payload.get("tone"),
objection_id=payload.get("objection_id"),
)
@router.get("/decisions/preferences")
async def decisions_preferences(customer_id: str) -> dict[str, Any]:
mem = _memory_for(customer_id)
return {"customer_id": customer_id, "preferences": mem.preferences()}