From 57b3a03371cefccb57ece1ad3c13be69e2dcc0ee Mon Sep 17 00:00:00 2001 From: Martin Guitteny <“martin.guitteny@centralesupelec.fr”> Date: Fri, 3 Oct 2025 17:46:18 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A9(agents)=20add=20feature=20flag=20f?= =?UTF-8?q?or=20metadata=20agent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change FeatureFlag class to accept featureflag from posthog. add metadata_agent featuer flag to check if the feature flag is enabled for the creator of the recording. This feature flag will be used to allow some chosen users to test the named diarization feature. --- src/backend/core/api/feature_flag.py | 68 +++++++++++++++---- .../recording/services/recording_events.py | 21 +++++- src/backend/pyproject.toml | 1 + 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/backend/core/api/feature_flag.py b/src/backend/core/api/feature_flag.py index b137a7ea..92eae28b 100644 --- a/src/backend/core/api/feature_flag.py +++ b/src/backend/core/api/feature_flag.py @@ -1,30 +1,74 @@ -"""Feature flag handler for the Meet core app.""" - +# core/api/feature_flag.py +import logging +import os from functools import wraps +from logging import getLogger from django.conf import settings from django.http import Http404 +logging.basicConfig(level=logging.DEBUG) + +logger = getLogger(__name__) + +from posthog import Posthog + + +class FeatureFlagError(Exception): + """Feature flag management error.""" + class FeatureFlag: - """Check if features are enabled and return error responses.""" + """Feature flag management using Django settings and PostHog.""" FLAGS = { - "recording": "RECORDING_ENABLE", - "storage_event": "RECORDING_STORAGE_EVENT_ENABLE", - "subtitle": "ROOM_SUBTITLE_ENABLED", + "metadata_agent": {"posthog": "is_metadata_agent_enabled"}, + "recording": {"setting": "RECORDING_ENABLE"}, + "storage_event": {"setting": "RECORDING_STORAGE_EVENT_ENABLE"}, + "subtitle": {"setting": "ROOM_SUBTITLE_ENABLED"}, } + _ph_client = None + @classmethod - def flag_is_active(cls, flag_name): + def _get_ph_client(cls): + """Initialize and return PostHog client if configured.""" + if cls._ph_client is not None: + return cls._ph_client + api_key = os.getenv("POSTHOG_API_KEY") + host = os.getenv("POSTHOG_API_HOST", "https://eu.i.posthog.com") + logging.info("PostHog config: api_key=%s, host=%s", api_key, host) + if Posthog and api_key and host: + logging.info("Initializing PostHog client") + cls._ph_client = Posthog(project_api_key=api_key, host=host) + logging.info("PostHog client initialized: %s", bool(cls._ph_client)) + return cls._ph_client + + @classmethod + def flag_is_active(cls, flag_name, *, distinct_id=None, default=False): """Check if a feature flag is active.""" - - setting_name = cls.FLAGS.get(flag_name) - - if setting_name is None: + cfg = cls.FLAGS.get(flag_name) + if not cfg: return False - return getattr(settings, setting_name, False) + setting_name = cfg.get("setting") + if setting_name is not None: + return bool(getattr(settings, setting_name, False)) + + posthog_flag = cfg.get("posthog") + if posthog_flag: + ph = cls._get_ph_client() + if ph and distinct_id: + try: + logger.info( + "Checking PostHog flag %s for id=%s", posthog_flag, distinct_id + ) + return bool(ph.feature_enabled(posthog_flag, distinct_id)) + except FeatureFlagError as e: + logging.error("Error checking feature flag %s: %s", flag_name, e) + return default + return default + return default @classmethod def require(cls, flag_name): diff --git a/src/backend/core/recording/services/recording_events.py b/src/backend/core/recording/services/recording_events.py index d448618a..cc1d52be 100644 --- a/src/backend/core/recording/services/recording_events.py +++ b/src/backend/core/recording/services/recording_events.py @@ -10,6 +10,7 @@ from django.db import DatabaseError, transaction import aiohttp from core import models, utils +from core.api.feature_flag import FeatureFlag from core.services.metadata import MetadataService logging.basicConfig(level=logging.DEBUG) @@ -21,6 +22,19 @@ class RecordingEventsError(Exception): """Recording event handling fails.""" +def get_recording_creator_id(recording: models.Recording) -> str | None: + """Get the user ID of the recording creator (owner).""" + owner = ( + models.RecordingAccess.objects.select_related("user") + .filter( + role=models.RoleChoices.OWNER, + recording_id=recording.id, + ) + .first() + ) + return str(owner.user.id) + + class RecordingEventsService: """Handles recording-related Livekit webhook events.""" @@ -61,9 +75,14 @@ class RecordingEventsService: @staticmethod def handle_egress_started(recording): """Start metadata agent after transaction commit.""" - service = MetadataService() rec_id = recording.id room_id = recording.room_id + creator_id = get_recording_creator_id(recording) + if not FeatureFlag.flag_is_active("metadata_agent", distinct_id=creator_id): + logger.info("Metadata agent disabled by PostHog flag for id=%s", creator_id) + return + + service = MetadataService() logger.info( "Scheduling metadata start for recording=%s room_id=%s", rec_id, room_id diff --git a/src/backend/pyproject.toml b/src/backend/pyproject.toml index 006058c7..2e1caed2 100644 --- a/src/backend/pyproject.toml +++ b/src/backend/pyproject.toml @@ -48,6 +48,7 @@ dependencies = [ "jsonschema==4.24.0", "markdown==3.8.2", "nested-multipart-parser==1.5.0", + "posthog==6.0.3", "psycopg[binary]==3.2.9", "PyJWT==2.10.1", "python-frontmatter==1.1.0",