Consolidate feature flags to ALWRITY_ENABLED_FEATURES

Backend:
- Add get_enabled_features() returning set from ALWRITY_ENABLED_FEATURES
- Update router registry to use 'features' instead of 'profiles'
- Support feature names: podcast, blog-writer, youtube, story-writer, etc
- Update bootstrap gating to use enabled features
- Update PODCAST_ONLY_DEMO_MODE to check new flag first
- Add backwards compatibility with legacy env vars

Frontend:
- Update demoMode.ts to use REACT_APP_ENABLED_FEATURES
- Add getEnabledFeatures() and isFeatureEnabled() utilities

Usage:
  ALWRITY_ENABLED_FEATURES=all          # All features (default)
  ALWRITY_ENABLED_FEATURES=podcast      # Podcast only
  ALWRITY_ENABLED_FEATURES=podcast,core # Podcast + core features
This commit is contained in:
ajaysi
2026-03-31 18:40:54 +05:30
parent 9f0298725a
commit cd06c6aaa8
5 changed files with 240 additions and 112 deletions

View File

@@ -26,42 +26,63 @@ class BootstrapResult:
LINGUISTIC_REQUIRED_FEATURES = {"content_planning", "strategy_copilot", "facebook", "linkedin", "blog_writer", "persona"}
def get_enabled_features() -> set:
"""Get enabled features from environment variable.
ALWRITY_ENABLED_FEATURES can be:
- "all" - enable all features (default)
- comma-separated list: "podcast,blog-writer,youtube"
- single feature: "podcast"
"""
env_value = os.getenv(
"ALWRITY_ENABLED_FEATURES",
os.getenv("ALWRITY_FEATURE_PROFILE", os.getenv("ALWRITY_ROUTER_PROFILE", "all"))
).strip().lower()
if not env_value or env_value == "all":
return {"all"}
return {f.strip() for f in env_value.split(",") if f.strip()}
def get_active_profile() -> str:
"""Get active profile from environment variables."""
return os.getenv("ALWRITY_ACTIVE_PROFILE", os.getenv("ALWRITY_PROFILE", os.getenv("ALWRITY_FEATURE_PROFILE", os.getenv("ALWRITY_ROUTER_PROFILE", os.getenv("ALWRITY_FEATURE_TO_ENABLE", "all"))))).strip().lower() or "all"
def get_loaded_features() -> set:
"""Get loaded features from environment variables."""
features_str = os.getenv("ALWRITY_LOADED_FEATURES", os.getenv("ALWRITY_ENABLED_FEATURES", os.getenv("ALWRITY_FEATURES", "")))
if not features_str:
return set()
return {f.strip().lower() for f in features_str.split(",") if f.strip()}
"""Legacy function - returns primary profile for backwards compatibility."""
enabled = get_enabled_features()
if "all" in enabled:
return "all"
return list(enabled)[0] if enabled else "all"
def should_bootstrap_linguistic_models() -> bool:
"""Decide whether to bootstrap linguistic models based on profile."""
profile = get_active_profile()
"""Decide whether to bootstrap linguistic models based on enabled features."""
enabled_features = get_enabled_features()
verbose = os.getenv("ALWRITY_VERBOSE", "false").lower() == "true"
if profile in {"all", "default"}:
if "all" in enabled_features:
return True
loaded_features = get_loaded_features()
if not loaded_features:
return False
# Map old profile names to features for backwards compatibility
feature_mapping = {
"podcast": "podcast",
"youtube": "youtube",
"planning": "content-planning",
"default": "all"
}
return bool(loaded_features & LINGUISTIC_REQUIRED_FEATURES)
# Check if any linguistic-required feature is enabled
linguistic_features = {"content_planning", "facebook", "linkedin", "blog-writer", "persona"}
return bool(enabled_features & linguistic_features)
def should_bootstrap_local_llm_models() -> bool:
"""Decide whether to bootstrap local LLM models based on profile."""
profile = get_active_profile()
"""Decide whether to bootstrap local LLM models based on enabled features."""
enabled_features = get_enabled_features()
if profile in {"all", "default"}:
if "all" in enabled_features:
return True
return profile not in {"podcast", "youtube", "planning"}
# Skip LLM bootstrap for lean deployments
return "core" in enabled_features or "podcast" in enabled_features
def bootstrap_linguistic_models() -> BootstrapResult: