mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-17 23:09:35 +00:00
From advanced prompts integration: - skill_registry.py: Domain skill system with registry + runtime + policy enforcement - autopilot.py: Safe autopilot with simulation/recommendation/approval-gated modes - escalation.py: Human-in-the-loop escalation with Arabic packets and resume tokens - signal_intelligence.py: Real-time signal ingestion, dedup, scoring, watchlists - alert_delivery.py: Multi-channel alerts (dashboard/WhatsApp/email/SMS) with digests - behavior_intelligence.py: Pattern detection, rep performance, winning sequences - intelligence.py: Updated API with signals, alerts, patterns, escalations endpoints https://claude.ai/code/session_01LsnvBa7HwF5hs99VZbgLGj
460 lines
14 KiB
Python
460 lines
14 KiB
Python
"""
|
|
Intelligence API — Signals, alerts, behaviour patterns, recommendations,
|
|
escalations. Wires the signal_intelligence, alert_delivery and
|
|
behavior_intelligence services into FastAPI endpoints.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
from typing import List, Optional
|
|
|
|
from fastapi import APIRouter, BackgroundTasks, HTTPException, Query
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.services.signal_intelligence import (
|
|
SignalSource,
|
|
SignalEvent,
|
|
SignalFilter,
|
|
Watchlist,
|
|
get_signal_intelligence,
|
|
)
|
|
from app.services.alert_delivery import (
|
|
AlertUrgency,
|
|
get_alert_delivery,
|
|
)
|
|
from app.services.behavior_intelligence import (
|
|
get_behavior_intelligence,
|
|
)
|
|
|
|
logger = logging.getLogger("dealix.api.intelligence")
|
|
|
|
router = APIRouter(prefix="/intelligence", tags=["Intelligence"])
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Request / Response schemas
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class IngestRequest(BaseModel):
|
|
source: SignalSource
|
|
payload: dict
|
|
tenant_id: str
|
|
|
|
|
|
class WatchlistCreate(BaseModel):
|
|
tenant_id: str
|
|
name: str
|
|
name_ar: str
|
|
entity_type: str
|
|
entity_ids: List[str] = []
|
|
keywords: List[str] = []
|
|
alert_threshold: float = 0.5
|
|
channels: List[str] = Field(default=["dashboard"])
|
|
|
|
|
|
class AcknowledgeRequest(BaseModel):
|
|
user_id: str
|
|
|
|
|
|
class EscalationResolveRequest(BaseModel):
|
|
user_id: str
|
|
resolution: str
|
|
resolution_ar: str = ""
|
|
|
|
|
|
class _Escalation(BaseModel):
|
|
id: str = Field(default_factory=lambda: uuid.uuid4().hex)
|
|
tenant_id: str
|
|
title: str
|
|
title_ar: str
|
|
reason: str
|
|
reason_ar: str
|
|
entity_type: str = ""
|
|
entity_id: str = ""
|
|
assigned_to: Optional[str] = None
|
|
status: str = "open" # open, resolved
|
|
resolved_by: Optional[str] = None
|
|
resolved_at: Optional[datetime] = None
|
|
resolution: Optional[str] = None
|
|
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
|
|
|
|
|
# In-memory escalation store (per-tenant)
|
|
_escalations: dict[str, list[_Escalation]] = {}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Signals
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@router.post("/signals/ingest", summary="Ingest a raw signal event")
|
|
async def ingest_signal(req: IngestRequest):
|
|
"""Normalise, score, deduplicate and store a signal from any source."""
|
|
engine = get_signal_intelligence()
|
|
event = await engine.ingest(req.source, req.payload, req.tenant_id)
|
|
return event.model_dump()
|
|
|
|
|
|
@router.get("/signals", summary="List recent signals with filters")
|
|
async def list_signals(
|
|
tenant_id: str,
|
|
source: Optional[SignalSource] = None,
|
|
entity_type: Optional[str] = None,
|
|
entity_id: Optional[str] = None,
|
|
min_importance: float = 0.0,
|
|
sentiment: Optional[str] = None,
|
|
limit: int = Query(default=50, le=200),
|
|
):
|
|
"""Return signals matching the provided filters, most recent first."""
|
|
engine = get_signal_intelligence()
|
|
filters = SignalFilter(
|
|
source=source,
|
|
entity_type=entity_type,
|
|
entity_id=entity_id,
|
|
min_importance=min_importance,
|
|
sentiment=sentiment,
|
|
limit=limit,
|
|
)
|
|
events = await engine.get_signals(tenant_id, filters)
|
|
return {
|
|
"count": len(events),
|
|
"signals": [e.model_dump() for e in events],
|
|
}
|
|
|
|
|
|
@router.get(
|
|
"/signals/{entity_type}/{entity_id}",
|
|
summary="Signal summary for a specific entity",
|
|
)
|
|
async def entity_signal_summary(
|
|
entity_type: str,
|
|
entity_id: str,
|
|
tenant_id: str,
|
|
hours: int = Query(default=24, le=720),
|
|
):
|
|
"""Summarise all signals for a given entity in the last N hours."""
|
|
engine = get_signal_intelligence()
|
|
summary = await engine.get_entity_summary(entity_type, entity_id, tenant_id, hours)
|
|
return summary
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Watchlists
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@router.post("/watchlists", summary="Create a signal watchlist")
|
|
async def create_watchlist(body: WatchlistCreate):
|
|
"""Create a watchlist that triggers alerts when matching signals arrive."""
|
|
engine = get_signal_intelligence()
|
|
wl = Watchlist(
|
|
tenant_id=body.tenant_id,
|
|
name=body.name,
|
|
name_ar=body.name_ar,
|
|
entity_type=body.entity_type,
|
|
entity_ids=body.entity_ids,
|
|
keywords=body.keywords,
|
|
alert_threshold=body.alert_threshold,
|
|
channels=body.channels,
|
|
)
|
|
created = await engine.create_watchlist(wl)
|
|
return created.model_dump()
|
|
|
|
|
|
@router.get("/watchlists", summary="List active watchlists")
|
|
async def list_watchlists(tenant_id: str):
|
|
engine = get_signal_intelligence()
|
|
items = await engine.get_watchlists(tenant_id)
|
|
return {"count": len(items), "watchlists": [w.model_dump() for w in items]}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Alerts
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@router.get("/alerts", summary="Pending alerts for a user")
|
|
async def pending_alerts(
|
|
tenant_id: str,
|
|
user_id: Optional[str] = None,
|
|
):
|
|
"""Return all unacknowledged alerts, optionally filtered by user."""
|
|
delivery = get_alert_delivery()
|
|
alerts = await delivery.get_pending(tenant_id, user_id)
|
|
return {
|
|
"count": len(alerts),
|
|
"alerts": [a.model_dump() for a in alerts],
|
|
}
|
|
|
|
|
|
@router.post("/alerts/{alert_id}/acknowledge", summary="Acknowledge an alert")
|
|
async def acknowledge_alert(alert_id: str, body: AcknowledgeRequest):
|
|
delivery = get_alert_delivery()
|
|
ok = await delivery.acknowledge(alert_id, body.user_id)
|
|
if not ok:
|
|
raise HTTPException(404, "التنبيه غير موجود")
|
|
return {"acknowledged": True, "alert_id": alert_id}
|
|
|
|
|
|
@router.get("/alerts/stats", summary="Alert delivery statistics")
|
|
async def alert_stats(tenant_id: str):
|
|
delivery = get_alert_delivery()
|
|
return await delivery.get_delivery_stats(tenant_id)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Digest
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@router.get("/digest", summary="Generate Arabic alert digest")
|
|
async def generate_digest(
|
|
tenant_id: str,
|
|
user_id: Optional[str] = None,
|
|
period: str = Query(default="daily", regex="^(daily|weekly)$"),
|
|
):
|
|
"""Compile unacknowledged alerts into an Arabic summary."""
|
|
delivery = get_alert_delivery()
|
|
return await delivery.generate_digest(tenant_id, user_id, period)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Behavior Patterns
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@router.get("/patterns", summary="Detected behaviour patterns")
|
|
async def detected_patterns(tenant_id: str):
|
|
"""Return all detected patterns: rep performance, sequences, risks."""
|
|
bi = get_behavior_intelligence()
|
|
|
|
rep = await bi.analyze_rep_performance(tenant_id)
|
|
seq = await bi.analyze_winning_sequences(tenant_id)
|
|
risk = await bi.detect_at_risk_patterns(tenant_id)
|
|
timing = await bi.analyze_best_contact_times(tenant_id)
|
|
|
|
all_patterns = [p.model_dump() for p in rep + seq + risk]
|
|
|
|
return {
|
|
"count": len(all_patterns),
|
|
"patterns": all_patterns,
|
|
"best_contact_times": timing,
|
|
}
|
|
|
|
|
|
@router.get("/recommendations", summary="AI recommendations in Arabic")
|
|
async def recommendations(tenant_id: str):
|
|
"""Generate actionable Arabic recommendations based on detected patterns."""
|
|
bi = get_behavior_intelligence()
|
|
recs = await bi.get_recommendations(tenant_id)
|
|
return {
|
|
"count": len(recs),
|
|
"recommendations": recs,
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Escalations
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@router.get("/escalations", summary="Pending escalations")
|
|
async def pending_escalations(tenant_id: str):
|
|
"""List all open escalations for a tenant."""
|
|
items = [e for e in _escalations.get(tenant_id, []) if e.status == "open"]
|
|
return {
|
|
"count": len(items),
|
|
"escalations": [e.model_dump() for e in items],
|
|
}
|
|
|
|
|
|
@router.post("/escalations/{escalation_id}/resolve", summary="Resolve an escalation")
|
|
async def resolve_escalation(escalation_id: str, body: EscalationResolveRequest):
|
|
"""Mark an escalation as resolved with a resolution note."""
|
|
for esc_list in _escalations.values():
|
|
for esc in esc_list:
|
|
if esc.id == escalation_id:
|
|
if esc.status == "resolved":
|
|
return {"already_resolved": True, "id": escalation_id}
|
|
esc.status = "resolved"
|
|
esc.resolved_by = body.user_id
|
|
esc.resolved_at = datetime.now(timezone.utc)
|
|
esc.resolution = body.resolution or body.resolution_ar
|
|
logger.info(
|
|
"Escalation %s resolved by %s",
|
|
escalation_id[:8], body.user_id[:8],
|
|
)
|
|
return {"resolved": True, "id": escalation_id}
|
|
|
|
raise HTTPException(404, "التصعيد غير موجود")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper: create escalation (called internally by signal/alert pipeline)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
async def create_escalation(
|
|
tenant_id: str,
|
|
title: str,
|
|
title_ar: str,
|
|
reason: str,
|
|
reason_ar: str,
|
|
entity_type: str = "",
|
|
entity_id: str = "",
|
|
assigned_to: Optional[str] = None,
|
|
) -> dict:
|
|
"""Programmatic escalation creation (not exposed as public endpoint)."""
|
|
esc = _Escalation(
|
|
tenant_id=tenant_id,
|
|
title=title,
|
|
title_ar=title_ar,
|
|
reason=reason,
|
|
reason_ar=reason_ar,
|
|
entity_type=entity_type,
|
|
entity_id=entity_id,
|
|
assigned_to=assigned_to,
|
|
)
|
|
_escalations.setdefault(tenant_id, []).insert(0, esc)
|
|
|
|
# Fire an alert via the delivery service
|
|
delivery = get_alert_delivery()
|
|
await delivery.send_from_template(
|
|
template_key="escalation",
|
|
tenant_id=tenant_id,
|
|
urgency=AlertUrgency.HIGH,
|
|
category="compliance",
|
|
user_id=assigned_to,
|
|
requires_ack=True,
|
|
title=title_ar,
|
|
reason=reason_ar,
|
|
)
|
|
|
|
logger.info("Escalation created: %s for tenant %s", esc.id[:8], tenant_id[:8])
|
|
return esc.model_dump()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Legacy endpoints preserved for backward compatibility
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class LeadInput(BaseModel):
|
|
id: str = "lead_001"
|
|
contact_name: str
|
|
contact_phone: str
|
|
contact_title: Optional[str] = None
|
|
company_name: str
|
|
company_website: Optional[str] = None
|
|
source: str = "whatsapp"
|
|
|
|
|
|
class MeetingReport(BaseModel):
|
|
lead_id: str
|
|
contact_name: str
|
|
company_name: str
|
|
contact_phone: str
|
|
meeting_notes: str
|
|
outcome: str = "follow_up_needed"
|
|
|
|
|
|
def _groq_key():
|
|
import os
|
|
key = os.getenv("GROQ_API_KEY", "")
|
|
if not key:
|
|
raise HTTPException(500, "GROQ_API_KEY missing")
|
|
return key
|
|
|
|
|
|
@router.post("/run-pipeline")
|
|
async def run_lead_pipeline(lead_input: LeadInput):
|
|
from app.services.lead_pipeline import DealixLeadPipeline, Lead, Company
|
|
pipeline = DealixLeadPipeline(_groq_key())
|
|
lead = Lead(
|
|
id=lead_input.id,
|
|
contact_name=lead_input.contact_name,
|
|
contact_phone=lead_input.contact_phone,
|
|
contact_title=lead_input.contact_title,
|
|
company=Company(name=lead_input.company_name, website=lead_input.company_website),
|
|
source=lead_input.source,
|
|
)
|
|
return await pipeline.run_full_pipeline(lead)
|
|
|
|
|
|
@router.post("/executive-report")
|
|
async def generate_executive_report(report_data: MeetingReport):
|
|
from app.services.lead_pipeline import DealixLeadPipeline, Lead, Company
|
|
pipeline = DealixLeadPipeline(_groq_key())
|
|
lead = Lead(
|
|
id=report_data.lead_id,
|
|
contact_name=report_data.contact_name,
|
|
contact_phone=report_data.contact_phone,
|
|
company=Company(name=report_data.company_name),
|
|
)
|
|
return await pipeline.generate_executive_report(
|
|
lead, report_data.meeting_notes, report_data.outcome,
|
|
)
|
|
|
|
|
|
@router.get("/system-report")
|
|
async def get_system_intelligence_report():
|
|
from app.services.autonomous_core import get_autonomous_core
|
|
core = get_autonomous_core(_groq_key())
|
|
return await core.get_full_intelligence_report()
|
|
|
|
|
|
@router.post("/improve")
|
|
async def trigger_self_improvement(background_tasks: BackgroundTasks):
|
|
from app.services.autonomous_core import get_autonomous_core
|
|
core = get_autonomous_core(_groq_key())
|
|
|
|
async def run_improvement():
|
|
await core.improver.analyze_and_improve({"triggered": "manual"})
|
|
|
|
background_tasks.add_task(run_improvement)
|
|
return {"status": "improvement_cycle_started", "message": "النظام يحلل نفسه ويتحسن..."}
|
|
|
|
|
|
@router.get("/financial-forecast")
|
|
async def get_financial_forecast():
|
|
from app.services.autonomous_core import get_autonomous_core
|
|
core = get_autonomous_core(_groq_key())
|
|
return await core.financial.generate_financial_forecast({
|
|
"timestamp": "now", "pipeline": "active",
|
|
})
|
|
|
|
|
|
@router.get("/market-expansion")
|
|
async def get_expansion_opportunities():
|
|
from app.services.autonomous_core import get_autonomous_core
|
|
core = get_autonomous_core(_groq_key())
|
|
return await core.strategic.analyze_market_opportunity({
|
|
"market": "Saudi Arabia",
|
|
"current_sectors": ["عقارات", "تقنية", "صحة"],
|
|
})
|
|
|
|
|
|
@router.get("/growth-plan")
|
|
async def get_90_day_growth_plan():
|
|
from app.services.autonomous_core import get_autonomous_core
|
|
core = get_autonomous_core(_groq_key())
|
|
return await core.strategic.generate_growth_plan({
|
|
"current_stage": "early_growth", "market": "KSA",
|
|
})
|
|
|
|
|
|
@router.get("/health")
|
|
async def system_health():
|
|
from app.services.autonomous_core import get_autonomous_core
|
|
core = get_autonomous_core(_groq_key())
|
|
return {
|
|
"health": core.healer.get_system_health(),
|
|
"autonomous_cycle": core._cycle_count,
|
|
"improvements_applied": len(core.improver.improvements_log),
|
|
"status": "AUTONOMOUS_RUNNING",
|
|
}
|