Skip to content

UN-3291 [FIX] Migrate Flipt v1 to v2 and fix gRPC client bugs for feature flags#1808

Open
muhammad-ali-e wants to merge 2 commits intomainfrom
fix/UN-3291-FIX_flipt_v2_migration_grpc_bugs
Open

UN-3291 [FIX] Migrate Flipt v1 to v2 and fix gRPC client bugs for feature flags#1808
muhammad-ali-e wants to merge 2 commits intomainfrom
fix/UN-3291-FIX_flipt_v2_migration_grpc_bugs

Conversation

@muhammad-ali-e
Copy link
Contributor

@muhammad-ali-e muhammad-ali-e commented Feb 27, 2026

What

  • Migrate Docker dev essentials Flipt from v1.34.0 to v2.3.1 with file-based storage
  • Fix FliptClient.evaluate_boolean() accessing non-existent result.value (should be result.enabled)
  • Fix check_feature_flag_status() calling .enabled on a bool return value
  • Pre-register timestamp_pb2 in flipt_grpc package to prevent protobuf descriptor errors in workers
  • Add check_feature_flag_variant() with full docstring for variant flag evaluation
  • Add evaluate_variant() and list_feature_flags() methods to FliptClient

Why

  • PR [FIX] SDK1 integration issues and Flipt migration changes #1665 (UN-2950) migrated the feature flag system to a custom gRPC client for Flipt v2, but left bugs that cause all feature flag evaluations to silently return False
  • Docker dev essentials was still on Flipt v1.34.0, inconsistent with the v2 client code
  • Workers lack implicit timestamp_pb2 import that backend gets from Google Cloud libraries
  • Variant flag support is needed for per-organization feature rollouts with configuration payloads

How

  • flipt.py: Changed result.valueresult.enabled to match the BooleanEvaluationResponse protobuf field
  • flipt.py: Added evaluate_variant() method that calls Flipt's VariantEvaluationRequest gRPC endpoint
  • flipt.py: Added list_feature_flags() method that calls ListFlagRequest gRPC endpoint to check flag existence/status
  • feature_flag.py: Changed bool(result.enabled)bool(result) since evaluate_boolean() already returns a bool
  • feature_flag.py: Added check_feature_flag_variant() — checks flag is enabled via list_feature_flags(), then evaluates variant and returns match/variant_key/variant_attachment/segment_keys
  • flipt_grpc/__init__.py: Added from google.protobuf import timestamp_pb2 to pre-register the well-known type
  • docker-compose-dev-essentials.yaml: Updated image to docker.flipt.io/flipt/flipt:v2.3.1, replaced env-based DB with volume mount
  • sample.essentials.env: Removed FLIPT_DB_URL (no longer needed in v2)

Can this PR break any existing features. If yes, please list possible items. If no, please explain why.

  • No breakage expected — this PR fixes currently broken feature flag evaluation
  • Flipt v2 is backward-compatible with v1 flag definitions
  • The timestamp_pb2 pre-registration is a no-op if already loaded
  • New check_feature_flag_variant() is additive; no existing callers are affected

Database Migrations

  • None

Env Config

  • Removed: FLIPT_DB_URL from sample.essentials.env (Flipt v2 uses file-based storage)

Related Issues or PRs

Dependencies Versions

  • Flipt: v1.34.0 → v2.3.1

Notes on Testing

  • Start dev essentials: verify Flipt v2 container starts and gRPC port 9005 is accessible
  • Test feature flag evaluation returns correct values (not always False)
  • Verify workers can import flipt_grpc without protobuf descriptor errors
  • Test check_feature_flag_variant() with a configured variant flag, segments, and rules

Checklist

I have read and understood the Contribution Guidelines.

…ture flags

Migrate Docker dev essentials Flipt from v1.34.0 to v2.3.1 with
file-based storage, and fix three gRPC client bugs introduced in
PR #1665 that caused all feature flag evaluations to silently
return False.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 27, 2026

Summary by CodeRabbit

  • Chores

    • Updated feature-flag service image to a newer release, added persistent data storage and exposed service port for load balancing.
    • Removed legacy environment-based configuration and related sample env entries.
  • New Features

    • Added variant-aware flag evaluation, returning structured variant details when available.
  • Bug Fixes

    • Fixed protobuf timestamp handling to prevent descriptor errors in worker/distributed environments.
  • Refactor

    • Adjusted flag evaluation to rely on the flag's enabled state for more accurate results.

Walkthrough

Flipt service image upgraded to v2.3.1 with env config removed, persistent volume added, and Traefik port label set. Flag evaluation now reads enabled and adds variant evaluation. Protobuf timestamp is pre-registered to avoid descriptor pool errors.

Changes

Cohort / File(s) Summary
Docker Configuration
docker/docker-compose-dev-essentials.yaml, docker/sample.essentials.env
Updated Flipt image to docker.flipt.io/flipt/flipt:v2.3.1; removed env_file/environment and FLIPT_CACHE_ENABLED; added flipt_data volume; added Traefik load-balancer port label; removed FLIPT_DB_URL and related encoding guidance from sample env.
Feature Flag Client & API
unstract/flags/src/unstract/flags/client/flipt.py, unstract/flags/src/unstract/flags/feature_flag.py
Changed boolean evaluation to use result.enabled; added evaluate_variant in client and check_feature_flag_variant in feature_flag to return structured variant results (match, variant_key, variant_attachment, segment_keys) with safe defaults and error handling.
Protobuf Init
unstract/flags/src/unstract/flags/flipt_grpc/flipt/__init__.py
Imported and referenced google.protobuf.timestamp_pb2 to pre-register the Timestamp well-known type before adding serialized protobuf descriptors, preventing descriptor pool KeyError/TypeError.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main changes: migrating Flipt from v1 to v2 and fixing gRPC client bugs affecting feature flags.
Description check ✅ Passed The description is comprehensive and addresses all required template sections with detailed explanations of what changed, why, implementation details, risk assessment, environment changes, and testing notes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/UN-3291-FIX_flipt_v2_migration_grpc_bugs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

Test Results

Summary
  • Runner Tests: 11 passed, 0 failed (11 total)
  • SDK1 Tests: 63 passed, 0 failed (63 total)

Runner Tests - Full Report
filepath function $$\textcolor{#23d18b}{\tt{passed}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_logs}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup\_skip}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_client\_init}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config\_without\_mount}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_run\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_for\_sidecar}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_sidecar\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{11}}$$ $$\textcolor{#23d18b}{\tt{11}}$$
SDK1 Tests - Full Report
filepath function $$\textcolor{#23d18b}{\tt{passed}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_success\_on\_first\_attempt}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retry\_on\_connection\_error}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_non\_retryable\_http\_error}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retryable\_http\_errors}}$$ $$\textcolor{#23d18b}{\tt{3}}$$ $$\textcolor{#23d18b}{\tt{3}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_post\_method\_retry}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retry\_logging}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_success\_on\_first\_attempt}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_retry\_on\_errors}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_wrapper\_methods\_retry}}$$ $$\textcolor{#23d18b}{\tt{4}}$$ $$\textcolor{#23d18b}{\tt{4}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_connection\_error\_is\_retryable}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_timeout\_is\_retryable}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_retryable\_status\_codes}}$$ $$\textcolor{#23d18b}{\tt{3}}$$ $$\textcolor{#23d18b}{\tt{3}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_non\_retryable\_status\_codes}}$$ $$\textcolor{#23d18b}{\tt{5}}$$ $$\textcolor{#23d18b}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_without\_response}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_os\_error\_retryable\_errno}}$$ $$\textcolor{#23d18b}{\tt{5}}$$ $$\textcolor{#23d18b}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_os\_error\_non\_retryable\_errno}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_other\_exception\_not\_retryable}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_exponential\_backoff\_without\_jitter}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_exponential\_backoff\_with\_jitter}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_max\_delay\_cap}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_max\_delay\_cap\_with\_jitter}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_successful\_call\_first\_attempt}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_retry\_after\_transient\_failure}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_max\_retries\_exceeded}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_retry\_with\_custom\_predicate}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_no\_retry\_with\_predicate\_false}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_exception\_not\_in\_tuple\_not\_retried}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_default\_configuration}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_environment\_variable\_configuration}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_max\_retries}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_base\_delay}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_multiplier}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_jitter\_values}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_custom\_exceptions\_only}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_custom\_predicate\_only}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_both\_exceptions\_and\_predicate}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_exceptions\_match\_but\_predicate\_false}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_retry\_platform\_service\_call\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_retry\_prompt\_service\_call\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_platform\_service\_decorator\_retries\_on\_connection\_error}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_prompt\_service\_decorator\_retries\_on\_timeout}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_warning\_logged\_on\_retry}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_info\_logged\_on\_success\_after\_retry}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_exception\_logged\_on\_giving\_up}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{63}}$$ $$\textcolor{#23d18b}{\tt{63}}$$

@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
unstract/flags/src/unstract/flags/feature_flag.py (2)

50-55: Unused namespace_key parameter.

The namespace_key parameter is declared but never used in the function body. The FliptClient reads the namespace from the UNSTRACT_FEATURE_FLAG_NAMESPACE environment variable internally.

For consistency with check_feature_flag_status (which also has this parameter), consider either:

  1. Removing it entirely if not needed for API compatibility
  2. Adding a deprecation warning like in FliptClient.evaluate_variant()
♻️ Option: Add deprecation warning for consistency
 def check_feature_flag_variant(
     flag_key: str,
     namespace_key: str | None = None,
     entity_id: str = "unstract",
     context: dict[str, str] | None = None,
 ) -> dict:
     ...
     """
     default_result = {
         "enabled": False,
         "match": False,
         "variant_key": "",
         "variant_attachment": "",
         "segment_keys": [],
     }
+    if namespace_key is not None:
+        import warnings
+        warnings.warn(
+            "namespace_key parameter is deprecated and will be ignored",
+            DeprecationWarning,
+            stacklevel=2,
+        )
     try:
         client = FliptClient()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@unstract/flags/src/unstract/flags/feature_flag.py` around lines 50 - 55,
Function check_feature_flag_variant declares namespace_key but never uses it;
either remove the parameter for simplicity or mirror check_feature_flag_status
by emitting a deprecation warning and ignoring the value. Update
check_feature_flag_variant to: if you keep the parameter, import warnings and
call warnings.warn(...) (match the message and deprecation style used in
FliptClient.evaluate_variant), then proceed without using namespace_key;
otherwise remove namespace_key from the signature and all callers. Reference
check_feature_flag_variant and FliptClient.evaluate_variant (and
check_feature_flag_status for consistency) when making the change.

45-45: Correct fix, but bool() wrapper is redundant.

Since FliptClient.evaluate_boolean() now returns a bool directly (via result.enabled if hasattr(result, "enabled") else False), the bool() wrapper is unnecessary.

♻️ Simplify by returning result directly
-        return bool(result)
+        return result
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@unstract/flags/src/unstract/flags/feature_flag.py` at line 45, The return
uses an unnecessary bool() wrapper — since FliptClient.evaluate_boolean()
already returns a bool, replace the redundant "return bool(result)" with "return
result" in the method in feature_flag.py (locate the function that currently
returns bool(result), e.g., the FeatureFlag or is_enabled wrapper that calls
FliptClient.evaluate_boolean()) so the boolean is returned directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@unstract/flags/src/unstract/flags/feature_flag.py`:
- Around line 50-55: Function check_feature_flag_variant declares namespace_key
but never uses it; either remove the parameter for simplicity or mirror
check_feature_flag_status by emitting a deprecation warning and ignoring the
value. Update check_feature_flag_variant to: if you keep the parameter, import
warnings and call warnings.warn(...) (match the message and deprecation style
used in FliptClient.evaluate_variant), then proceed without using namespace_key;
otherwise remove namespace_key from the signature and all callers. Reference
check_feature_flag_variant and FliptClient.evaluate_variant (and
check_feature_flag_status for consistency) when making the change.
- Line 45: The return uses an unnecessary bool() wrapper — since
FliptClient.evaluate_boolean() already returns a bool, replace the redundant
"return bool(result)" with "return result" in the method in feature_flag.py
(locate the function that currently returns bool(result), e.g., the FeatureFlag
or is_enabled wrapper that calls FliptClient.evaluate_boolean()) so the boolean
is returned directly.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 4593fb5 and 2bff694.

📒 Files selected for processing (2)
  • unstract/flags/src/unstract/flags/client/flipt.py
  • unstract/flags/src/unstract/flags/feature_flag.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant