system-prompts-and-models-o.../dealix/auto_client_acquisition/personal_operator/operator.py
Dealix Builder e34cc729aa feat(dealix): py3.10/3.11 compat shim + 54 unit tests for business/innovation/ai
PROBLEM
The codebase used Python 3.11+ stdlib features (`from datetime import UTC`,
`from enum import StrEnum`) in 22 files, breaking local dev on Python 3.10
(Windows users) and any pytest run that imports the affected modules.

SOLUTION
1. New `core/_py_compat.py` providing UTC + StrEnum shims that:
   - On 3.11+ re-export the stdlib names (zero overhead)
   - On 3.10 fall back to `timezone.utc` and a (str, Enum) backport

2. All 22 affected files patched to import from the shim:
   - core/utils.py, core/config/models.py
   - api/routers/admin.py
   - auto_client_acquisition/{ai/model_router, agents/{intake,icp_matcher},
     v3/{memory,agents,compliance_os,market_radar},
     personal_operator/{operator,memory,launch_report},
     innovation/{proof_ledger_repo,command_feed_live}}.py
   - autonomous_growth/agents/sector_intel.py
   - dealix/{trust/{approval,tool_verification,policy},
     observability/cost_tracker,
     contracts/{evidence_pack,event_envelope,audit_log,decision},
     classifications/__init__,
     governance/approvals}.py

3. Three new test suites for previously-untested layers (54 tests):
   - tests/unit/test_business_suite.py — gtm_plan, launch_metrics,
     market_positioning, pricing_strategy, proof_pack, unit_economics,
     verticals (28 tests covering plan recommendation, performance fee,
     ROI math, account health grading, vertical playbook structure)
   - tests/unit/test_innovation_suite.py — aeo_radar, command_feed,
     deal_rooms, experiments, growth_missions, proof_ledger, ten_in_ten
     (18 tests covering deterministic reproducibility, card type taxonomy,
     pending-approval invariant, kill-mission visibility)
   - tests/unit/test_ai_model_router.py — ModelTask + get_model_route +
     estimate_model_cost_class + requires_guardrail (8 tests covering
     enum integrity, route round-trip, guardrail bool contract)

VERIFICATION
- ast.parse green on all 22 patched files
- pytest tests/unit/ → 477 passed, 2 skipped (provider smoke needs API keys)
  on Python 3.10.12 venv with project requirements installed
- No behavior change on 3.11+: the shim re-exports stdlib symbols

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

348 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Arabic Personal Strategic Operator core.
This module powers a Boardy-style operator for Sami, but specialized for Dealix:
- Arabic-first daily strategic brief
- relationship and intro suggestions
- accept / skip / draft / schedule actions
- project-aware next steps
- safe execution guardrails
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from core._py_compat import UTC
from core._py_compat import StrEnum
from typing import Any
from uuid import uuid4
class OpportunityType(StrEnum):
CUSTOMER = "customer"
PARTNER = "partner"
ADVISOR = "advisor"
INVESTOR = "investor"
TALENT = "talent"
MEDIA = "media"
TECHNICAL = "technical"
INTERNAL_PROJECT = "internal_project"
class ApprovalDecision(StrEnum):
ACCEPT = "accept"
SKIP = "skip"
DRAFT = "draft"
SCHEDULE = "schedule"
NEEDS_RESEARCH = "needs_research"
@dataclass(frozen=True)
class OperatorProfile:
user_id: str
name: str
language: str = "ar"
timezone: str = "Asia/Riyadh"
primary_goal: str = "Launch Dealix as the Saudi B2B Revenue OS"
style: str = "direct, strategic, Arabic-first, execution-focused"
preferred_channels: list[str] = field(default_factory=lambda: ["whatsapp", "gmail", "calendar", "github"])
current_priorities: list[str] = field(default_factory=list)
avoid: list[str] = field(default_factory=lambda: ["cold WhatsApp without opt-in", "auto LinkedIn DM", "sending without approval"])
@dataclass(frozen=True)
class StrategicOpportunity:
title: str
opportunity_type: OpportunityType
person_or_company: str
why_now: str
strategic_value: str
recommended_action: str
suggested_message_ar: str
risk_notes: list[str] = field(default_factory=list)
evidence: list[str] = field(default_factory=list)
score: int = 70
id: str = field(default_factory=lambda: f"opp_{uuid4().hex[:12]}") # prefer stable ids from suggest_opportunities()
def to_card(self) -> dict[str, Any]:
return {
"id": self.id,
"title": self.title,
"type": self.opportunity_type.value,
"person_or_company": self.person_or_company,
"score": self.score,
"why_now": self.why_now,
"strategic_value": self.strategic_value,
"recommended_action": self.recommended_action,
"message_ar": self.suggested_message_ar,
"risk_notes": self.risk_notes,
"evidence": self.evidence,
"actions": {
"accept": {"key": ApprovalDecision.ACCEPT.value, "label_ar": "قبول"},
"skip": {"key": ApprovalDecision.SKIP.value, "label_ar": "تخطي"},
"draft": {"key": ApprovalDecision.DRAFT.value, "label_ar": "اكتب رسالة"},
"schedule": {"key": ApprovalDecision.SCHEDULE.value, "label_ar": "احجز اجتماع"},
"needs_research": {"key": ApprovalDecision.NEEDS_RESEARCH.value, "label_ar": "يحتاج بحث"},
},
"action_buttons": [
{"key": ApprovalDecision.ACCEPT.value, "label_ar": "قبول"},
{"key": ApprovalDecision.SKIP.value, "label_ar": "تخطي"},
{"key": ApprovalDecision.DRAFT.value, "label_ar": "رسالة"},
],
}
@dataclass(frozen=True)
class DailyBrief:
greeting: str
top_decisions: list[str]
opportunities: list[StrategicOpportunity]
risks: list[str]
launch_readiness: dict[str, Any]
generated_at: datetime = field(default_factory=lambda: datetime.now(UTC))
def to_dict(self) -> dict[str, Any]:
return {
"generated_at": self.generated_at.isoformat(),
"greeting": self.greeting,
"top_decisions": self.top_decisions,
"opportunities": [item.to_card() for item in self.opportunities],
"risks": self.risks,
"launch_readiness": self.launch_readiness,
}
def default_sami_profile() -> OperatorProfile:
return OperatorProfile(
user_id="sami",
name="سامي",
current_priorities=[
"Merge v3 Autonomous Revenue OS foundation",
"Build Arabic Personal Operator with Accept/Skip flow",
"Launch private beta for Saudi B2B founders",
"Connect Supabase project memory and WhatsApp approvals",
],
)
def suggest_opportunities(profile: OperatorProfile | None = None) -> list[StrategicOpportunity]:
"""Deterministic 37 opportunities with stable ids for WhatsApp / approvals."""
profile = profile or default_sami_profile()
_ = profile # reserved for future personalization
return [
StrategicOpportunity(
id="opp_internal_project",
title="تشغيل بوتك الشخصي العربي",
opportunity_type=OpportunityType.INTERNAL_PROJECT,
person_or_company="Dealix Personal Operator",
why_now="لأن PR v3 صار عنده Revenue Memory وProject Intelligence، والطبقة الناقصة الآن هي واجهة تنفيذ شخصية لك.",
strategic_value="يحول Dealix من نظام داخلي إلى مساعد يومي يقرر ويقترح وينفذ بموافقتك.",
recommended_action="اعتمد بناء Personal Operator كأول تجربة تشغيلية قبل بيعها للعملاء.",
suggested_message_ar="ابدأ بتشغيل النسخة الأولى: daily brief + فرص استراتيجية + قبول/تخطي + draft للرسائل.",
risk_notes=["لا ترسل أي رسالة خارجية تلقائياً قبل الموافقة", "ابدأ بـ Gmail draft وCalendar draft فقط"],
evidence=["Revenue Memory موجود", "Project Intelligence موجود", "v3 API router موجود"],
score=96,
),
StrategicOpportunity(
id="opp_customer_beta",
title="إطلاق Private Beta محدود",
opportunity_type=OpportunityType.CUSTOMER,
person_or_company="10 مؤسسين B2B سعوديين",
why_now="المنتج صار لديه قصة واضحة: Saudi Revenue OS + Personal Operator + Market Radar.",
strategic_value="يجلب feedback حقيقي وcase studies قبل الإطلاق العام.",
recommended_action="جهز قائمة 10 مؤسسين وابدأ بدعوة شخصية مع وعد واضح: 7 أيام لاكتشاف فرص نمو.",
suggested_message_ar="أبغى أعطيك وصول مبكر لتجربة Dealix: نظام يكتشف فرص B2B ويقترح لك next actions بالعربي. هل يناسبك نجربه 7 أيام؟",
risk_notes=["لا توسع قبل وجود onboarding واضح", "لا تعد بنتائج مضمونة قبل قياس pilot"],
evidence=["Command Center snapshot", "Market Radar demo", "Revenue Science forecast"],
score=91,
),
StrategicOpportunity(
id="opp_supabase_devops",
title="شريك Supabase/DevOps لإغلاق جاهزية الإنتاج",
opportunity_type=OpportunityType.TECHNICAL,
person_or_company="Supabase/Postgres engineer",
why_now="أضفنا schema للـ project memory، والمرحلة القادمة تحتاج تنفيذ embeddings/jobs/RLS بشكل production.",
strategic_value="يقلل مخاطر البيانات والـ launch ويجعل البوت يفهم المشروع فعلياً.",
recommended_action="ابحث عن مهندس Supabase/pgvector لمراجعة migration وRLS وembedding pipeline.",
suggested_message_ar="أبغى رأيك التقني في schema لـ Supabase/pgvector لمشروع AI Revenue OS. هل تقدر تراجع معي readiness خلال 30 دقيقة؟",
risk_notes=["RLS يجب اختباره قبل بيانات عملاء حقيقية", "لا تخزن أسرار أو tokens داخل embeddings"],
evidence=["supabase migration", "project_intelligence.py"],
score=88,
),
StrategicOpportunity(
id="opp_strategic_advisor",
title="مستشار استراتيجي لتموضع Dealix",
opportunity_type=OpportunityType.ADVISOR,
person_or_company="مستشار GTM سعودي",
why_now="قبل التوسع في المبيعات تحتاج قصة موحّدة: Revenue OS مقابل CRM أو أدوات الرسائل.",
strategic_value="يقلل وقت البيع ويرفع جودة المحادثات مع المؤسسين.",
recommended_action="جلسة 60 دقيقة لمراجعة ICP والرسالة والتسعير.",
suggested_message_ar="أبغى رأيك في تموضع Dealix كـ Saudi B2B Revenue OS. هل تفضّل جلسة أونلاين الأسبوع القادم؟",
risk_notes=["لا تلتزم بعقود طويلة قبل pilot"],
evidence=["pricing router", "market radar demo"],
score=84,
),
StrategicOpportunity(
id="opp_partner_channel",
title="شريك توزيع (وكالة/استوديو SaaS)",
opportunity_type=OpportunityType.PARTNER,
person_or_company="شريك محتمل في الرياض/الدمام",
why_now="الوصول لأول 10 عملاء أسرع عبر قنوات موثوقة من فريق واحد.",
strategic_value="توسيع الوصول مع الحفاظ على جودة التنفيذ.",
recommended_action="قائمة قصيرة من 5 شركاء + رسالة شراكة draft للموافقة.",
suggested_message_ar="نبحث عن شريك لإطلاق Dealix لشركات B2B السعودية. هل يهمكم استكشاف شراكة غير حصرية؟",
risk_notes=["تأكد من توافق العلامة التجارية والامتثال"],
evidence=["public router", "landing pages"],
score=79,
),
StrategicOpportunity(
id="opp_tech_reviewer",
title="مراجع تقني للمنتج (Product + API)",
opportunity_type=OpportunityType.TECHNICAL,
person_or_company="CTO مستقل أو lead engineer",
why_now="قبل beta عام تحتاج مراجعة أمان وAPI واختبارات.",
strategic_value="يقلل الدين التقني ويكشف ثغرات الـ auth والـ rate limits.",
recommended_action="جولة مراجعة 90 دقيقة + قائمة issues.",
suggested_message_ar="أبغى مراجعة سريعة لـ API ومسارات الموافقة في Dealix. هل عندك وقت لجلسة مراجعة؟",
risk_notes=["لا تشارك أسرار إنتاج؛ استخدم بيئة staging"],
evidence=["api/main.py", "tests/integration"],
score=77,
),
StrategicOpportunity(
id="opp_first_segment",
title="أول قطاع عميل: عيادات/عقار B2B",
opportunity_type=OpportunityType.CUSTOMER,
person_or_company="قطاع محدد في الرياض",
why_now="الرادار يظهر إشارات قطاعية؛ التركيز يحسّن التحويل.",
strategic_value="قصص نجاح واضحة لنفس النموذج الاقتصادي.",
recommended_action="اختر قطاعاً واحداً وابنِ playbook قصير.",
suggested_message_ar="نستهدف [قطاع] في الرياض لمرحلة pilot. هل تود أن نرسل لك تفاصيل البرنامج؟",
risk_notes=["لا تخلط رسائل متعددة القطاعات في نفس الحملة"],
evidence=["sectors router", "market radar"],
score=73,
),
]
def launch_readiness_score() -> dict[str, Any]:
checks = {
"core_api": 80,
"revenue_memory": 75,
"personal_operator": 45,
"supabase_vector_memory": 55,
"whatsapp_approval_flow": 35,
"gmail_calendar_execution": 25,
"frontend_command_center": 45,
"tests_ci": 40,
"observability": 45,
"security_pdpl": 55,
"billing_pricing": 50,
"onboarding": 35,
}
score = round(sum(checks.values()) / len(checks), 1)
stage = "private_beta_ready_after_fixes" if score >= 70 else "foundation_ready_not_launch_ready"
return {
"score": score,
"stage": stage,
"checks": checks,
"next_critical_path": [
"Merge PR #125 after tests",
"Add Personal Operator persistence + WhatsApp buttons",
"Connect Supabase embeddings pipeline",
"Add Gmail draft + Calendar schedule with approval",
"Ship private beta to 10 founders",
],
}
def build_daily_brief(profile: OperatorProfile | None = None) -> DailyBrief:
profile = profile or default_sami_profile()
return DailyBrief(
greeting=f"صباح الخير {profile.name}. هذا موجزك التنفيذي لليوم.",
top_decisions=[
"ادمج PR v3 بعد إضافة اختبارات smoke أساسية.",
"ابدأ Personal Operator كواجهة التشغيل اليومية قبل أي توسع في الميزات.",
"لا تطلق عام قبل WhatsApp approval + Gmail draft + Calendar schedule.",
],
opportunities=suggest_opportunities(profile),
risks=[
"الخطر الأكبر: كثرة الأدوات بدون workflow إنتاجي واضح.",
"الخطر الثاني: إرسال رسائل تلقائية قبل ضبط الموافقات والامتثال.",
"الخطر الثالث: إطلاق عام قبل وجود onboarding وتجربة pilot قابلة للقياس.",
],
launch_readiness=launch_readiness_score(),
)
def draft_intro_message(opportunity: StrategicOpportunity, tone: str = "warm") -> dict[str, Any]:
opener = "السلام عليكم" if tone == "formal" else "هلا"
body = (
f"{opener}، عندي مشروع اسمه Dealix نبنيه كـ Saudi B2B Revenue OS. "
f"سبب تواصلي أن {opportunity.why_now} "
"أبغى آخذ رأيك/نصيحتك بشكل مختصر، وليس عرض بيع طويل. يناسبك مكالمة 20 دقيقة؟"
)
send_at = datetime.now(UTC) + timedelta(hours=4)
return {
"channel_recommendation": "gmail_draft_first_then_whatsapp_after_opt_in",
"subject": f"رأيك في Dealix — {opportunity.title}",
"body_ar": body,
"approval_required": True,
"risk_notes": opportunity.risk_notes,
"suggested_send_window": send_at.isoformat(),
}
def draft_follow_up(meeting_title: str, outcome: str, next_step: str) -> dict[str, Any]:
return {
"subject": f"متابعة: {meeting_title}",
"body_ar": (
"شكراً على وقتك اليوم.\n\n"
f"أبرز ما خرجت به: {outcome}\n"
f"الخطوة المقترحة: {next_step}\n\n"
"إذا مناسب، أرسل لك ملخص قصير أو نحدد موعد متابعة الأسبوع القادم."
),
"approval_required": True,
"recommended_send_window": (datetime.now(UTC) + timedelta(hours=2)).isoformat(),
}
def apply_decision(opportunity: StrategicOpportunity, decision: ApprovalDecision) -> dict[str, Any]:
if decision == ApprovalDecision.ACCEPT:
msg = draft_intro_message(opportunity)
return {
"status": "accepted",
"next_action": "draft_message",
"draft_message": msg,
"approval_required": True,
"note": "لا يُرسل خارجياً إلا بعد موافقتك الصريحة.",
}
if decision == ApprovalDecision.SKIP:
return {
"status": "skipped",
"next_action": "learn_preference",
"approval_required": False,
"note": "سنقلل فرص مشابهة لاحقاً.",
}
if decision == ApprovalDecision.DRAFT:
msg = draft_intro_message(opportunity)
return {
"status": "draft_ready",
"next_action": "review_message_draft",
"draft_message": msg,
"approval_required": True,
}
if decision == ApprovalDecision.SCHEDULE:
return {
"status": "schedule_requested",
"next_action": "create_calendar_draft",
"approval_required": True,
"duration_minutes": 30,
"note": "إنشاء حدث تقويم فعلي يتطلب طبقة موافقة صريحة.",
}
return {
"status": "needs_research",
"next_action": "collect_more_context",
"approval_required": False,
"note": "جمّع أدلة إضافية قبل المسودة أو الجدولة.",
}