system-prompts-and-models-o.../salesflow-saas/backend/app/services/feature_flags.py
Claude 83210b9d12
feat: Add founder strategy, Claude Code control plane, SaaS launch readiness
Founder Strategy & GTM (from prompts #1, #10):
- niche-brief.md: Saudi real estate primary, healthcare secondary
- icp-brief.md: Full ICP with Arabic objection handling
- content-map.md: 20 content ideas, SEO keywords, weekly schedule
- outreach-map.md: WhatsApp/Email cold outreach with Arabic templates
- launch-plan.md: 14-day sprint + 30-day plan with revenue targets
- interview-template.md: 15 Arabic customer discovery questions

Claude Code Control Plane (from prompt #2):
- .claude/settings.json: Permissions and preferences
- .claude/commands/: 5 custom commands (review-pr, release-prep, security-check, generate-tests, architecture-review)
- .claude/hooks/: pre-commit.sh (secrets check), pre-push.sh (tests)

SaaS Launch Readiness (from prompt #4):
- saas-readiness-audit.md: Full audit with gap analysis
- deployment-checklist.md: Deploy + rollback procedures
- launch-checklist.md: 100+ launch day checklist items
- feature_flags.py: Redis-backed feature flags with per-tenant control

https://claude.ai/code/session_01LsnvBa7HwF5hs99VZbgLGj
2026-04-11 08:09:50 +00:00

160 lines
5.6 KiB
Python

"""
Feature Flags — Dealix AI Revenue OS
Simple Redis-backed feature flag system with per-tenant control.
"""
import logging
from typing import Optional
logger = logging.getLogger(__name__)
# Default flags and their initial state
DEFAULT_FLAGS = {
"ai_sales_agent": False, # Autonomous WhatsApp sales agent
"sequences": True, # Multi-channel sequences
"cpq": True, # Configure, Price, Quote
"signal_intelligence": False, # Real-time signal engine
"autopilot": False, # Autopilot automation
"behavior_intelligence": False,# Pattern detection
"arabic_nlp": True, # Arabic NLP processing
"lead_scoring_ai": True, # AI-powered lead scoring
"conversation_intel": False, # Conversation analysis
"territory_management": True, # Saudi territory routing
"whatsapp_chatbot": False, # Auto-reply chatbot
"pdpl_strict_mode": True, # Strict PDPL enforcement
"forecasting": False, # Sales forecasting
"alert_delivery": True, # Multi-channel alerts
"skill_registry": False, # Domain skill system
}
class FeatureFlagService:
"""
Feature flag service using Redis for fast lookups.
Supports global flags and per-tenant overrides.
"""
def __init__(self, redis_client=None):
self._redis = redis_client
self._local_cache: dict[str, bool] = dict(DEFAULT_FLAGS)
self._tenant_cache: dict[str, dict[str, bool]] = {}
def _global_key(self, flag: str) -> str:
return f"ff:global:{flag}"
def _tenant_key(self, flag: str, tenant_id: str) -> str:
return f"ff:tenant:{tenant_id}:{flag}"
async def is_enabled(
self, flag: str, tenant_id: Optional[str] = None
) -> bool:
"""Check if a feature flag is enabled."""
# Check tenant-specific override first
if tenant_id and self._redis:
try:
val = await self._redis.get(self._tenant_key(flag, tenant_id))
if val is not None:
return val == "1"
except Exception as e:
logger.warning(f"Redis error checking flag {flag}: {e}")
# Check global flag in Redis
if self._redis:
try:
val = await self._redis.get(self._global_key(flag))
if val is not None:
return val == "1"
except Exception as e:
logger.warning(f"Redis error checking flag {flag}: {e}")
# Fallback to local cache / defaults
return self._local_cache.get(flag, False)
async def enable(
self, flag: str, tenant_id: Optional[str] = None
) -> bool:
"""Enable a feature flag globally or for a specific tenant."""
key = (
self._tenant_key(flag, tenant_id)
if tenant_id
else self._global_key(flag)
)
if self._redis:
try:
await self._redis.set(key, "1")
except Exception as e:
logger.error(f"Redis error enabling flag {flag}: {e}")
return False
if not tenant_id:
self._local_cache[flag] = True
logger.info(
f"Feature flag '{flag}' enabled"
+ (f" for tenant {tenant_id}" if tenant_id else " globally")
)
return True
async def disable(
self, flag: str, tenant_id: Optional[str] = None
) -> bool:
"""Disable a feature flag globally or for a specific tenant."""
key = (
self._tenant_key(flag, tenant_id)
if tenant_id
else self._global_key(flag)
)
if self._redis:
try:
await self._redis.set(key, "0")
except Exception as e:
logger.error(f"Redis error disabling flag {flag}: {e}")
return False
if not tenant_id:
self._local_cache[flag] = False
logger.info(
f"Feature flag '{flag}' disabled"
+ (f" for tenant {tenant_id}" if tenant_id else " globally")
)
return True
async def list_flags(
self, tenant_id: Optional[str] = None
) -> dict[str, bool]:
"""List all flags with their current state."""
result = {}
for flag in DEFAULT_FLAGS:
result[flag] = await self.is_enabled(flag, tenant_id)
return result
async def enable_beta(self, tenant_id: str) -> dict[str, bool]:
"""Enable all beta features for a tenant (beta tester program)."""
beta_flags = [
"ai_sales_agent", "signal_intelligence", "autopilot",
"behavior_intelligence", "conversation_intel",
"forecasting", "skill_registry", "whatsapp_chatbot",
]
enabled = {}
for flag in beta_flags:
await self.enable(flag, tenant_id)
enabled[flag] = True
logger.info(f"Beta features enabled for tenant {tenant_id}")
return enabled
async def init_defaults(self) -> None:
"""Initialize default flag values in Redis."""
if not self._redis:
return
for flag, default_val in DEFAULT_FLAGS.items():
key = self._global_key(flag)
try:
exists = await self._redis.exists(key)
if not exists:
await self._redis.set(key, "1" if default_val else "0")
except Exception as e:
logger.warning(f"Could not init flag {flag}: {e}")
logger.info(f"Initialized {len(DEFAULT_FLAGS)} feature flags")
# Global singleton (initialize with Redis client at app startup)
feature_flags = FeatureFlagService()