Skip to content

Devcycle enhancements#426

Open
priyanshi-2003 wants to merge 1 commit intojuspay:releasefrom
priyanshi-2003:devcycle-enhancements
Open

Devcycle enhancements#426
priyanshi-2003 wants to merge 1 commit intojuspay:releasefrom
priyanshi-2003:devcycle-enhancements

Conversation

@priyanshi-2003
Copy link
Contributor

@priyanshi-2003 priyanshi-2003 commented Dec 14, 2025

Devcycle enhancements

Summary by CodeRabbit

Release Notes

  • New Features

    • Expanded speech recognition and synthesis options with support for multiple providers (Sarvam, Deepgram, Soniox, AssemblyAI).
    • Introduced user-level feature flags and A/B testing for targeted feature rollouts and experimentation.
    • Enhanced Daily.co integration with improved reliability for WebRTC connections.
  • Bug Fixes

    • Resolved proxy configuration issues affecting voice service operations.
  • Documentation

    • Added comprehensive architecture documentation for feature flag evaluation and targeting system.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 14, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces Redis-backed DevCycle feature flagging with local rule evaluation, extends the configuration system to support user-context-aware AB testing and targeting, integrates Sarvam TTS/STT providers, adds Soniox STT with context parsing, separates HTTP sessions for Daily.co proxy compatibility, and implements background task scheduling with Langfuse monitoring.

Changes

Cohort / File(s) Summary
DevCycle Feature Flagging Infrastructure
app/api/routers/devcycle.py, app/services/live_config/rule_evaluator.py, app/services/live_config/store.py, docs/DEVCYCLE_ARCHITECTURE.md
Introduces webhook-authenticated DevCycle integration with local rule evaluation, deterministic SHA256-based user bucketing (0–99), targeting audience matching (email, user_id, customData), and variation assignment; includes Redis-backed flag caching with atomic updates and verification helpers.
Configuration System Enhancement
app/core/config/dynamic.py, app/core/config/user_config.py
Extends dynamic config accessors to accept optional user context (user_id, user_email, custom_data) for per-user AB testing and targeting; introduces get_config_for_user and convenience wrappers (ENABLE_BREEZE_MCP_FOR_USER, SARVAM_TTS_MODEL_FOR_USER, etc.) for user-level feature flags; replaces BB_SARVAM_* with unified SARVAM_* variants.
Voice Agent STT/TTS Integration
app/agents/voice/automatic/stt/__init__.py, app/agents/voice/automatic/tts/__init__.py, app/ai/voice/agents/automatic/utils/common.py
Adds Sarvam, Soniox, and assemblyai STT providers with dynamic config fetching from Redis; introduces SarvamServiceType enum and get_sarvam_language utility; implements Soniox context parsing; extends TTS to include Sarvam service construction with language/pitch/pace configuration.
Voice Agent Initialization
app/ai/voice/agents/automatic/__init__.py
Enhances ENABLE_BREEZE_MCP call in run_normal_mode to derive and propagate user identifier and custom data (shop_id, user_name) for user-aware AB testing context.
Daily.co Proxy Handling
DAILY_PROXY_FIX.md, app/main.py
Separates aiohttp sessions: dedicated daily_aiohttp_session for Daily.co (no proxy) and separate session for other APIs (with proxy); updates DailyRESTHelper initialization and lifecycle management.
Background Tasks & Monitoring
app/main.py, app/core/logger/__init__.py
Integrates Langfuse task initialization on startup when background tasks enabled; adds BackgroundTaskScheduler lifecycle hook; updates log filtering for spam reduction.
Redis Client Infrastructure
app/services/redis/client.py
Introduces RedisFactory supporting both single-node and cluster modes with switchable initialization and connection testing; adds incr, expire operations and enhanced error handling.
Static Configuration & Environment
app/core/config/static.py, .env.example, requirements.txt
Adds DevCycle, Slack, background task, and Sarvam config constants; includes unresolved merge conflict markers requiring manual resolution.
Examples & Documentation
app/examples/hybrid_ab_testing_examples.py
Provides comprehensive examples of hybrid AB testing workflows with user-level config APIs, targeting scenarios, and feature flag usage patterns.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Webhook as DevCycle Webhook
    participant Handler as Webhook Handler<br/>(devcycle.py)
    participant CDN as DevCycle CDN
    participant Redis as Redis Store
    participant Evaluator as Rule Evaluator<br/>(rule_evaluator.py)
    participant Config as Config System

    Webhook->>Handler: POST /webhook<br/>(feature_key, event_type)
    Handler->>Handler: verify_webhook_secret()
    Handler->>CDN: Fetch full config
    CDN-->>Handler: All flags + targeting rules
    Handler->>Redis: Store flags_with_targeting
    activate Redis
    Redis-->>Handler: Atomic update complete
    deactivate Redis
    Handler-->>Webhook: 200 OK<br/>(feature_key, duration)

    Note over Config,Evaluator: On config request...
    Config->>Redis: get_config(key,<br/>user_id, user_email,<br/>custom_data)
    Redis-->>Config: flags_with_targeting
    Config->>Evaluator: get_variation_for_user()
    Evaluator->>Evaluator: get_user_bucket()<br/>(SHA256)
    Evaluator->>Evaluator: user_matches_audience()
    Evaluator->>Evaluator: get_variation_from<br/>_distribution()
    Evaluator-->>Config: variation_value
    Config-->>Config: Return user-specific<br/>or global value

    rect rgb(220, 240, 255)
        Note over Evaluator: Deterministic bucketing (0-99)<br/>Email/user_id/customData targeting<br/>Cached audience matching
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • Merge conflict resolution – Files contain unresolved git merge markers (.env.example, app/core/config/static.py, requirements.txt, app/services/redis/client.py, app/core/logger/__init__.py) that must be manually resolved before merge.
  • DevCycle rule evaluator logic – SHA256-based deterministic bucketing, audience matching with multiple operators (equals, contains, endsWith, in), and variation assignment; verify correctness of bucket distribution and operator implementations.
  • User-context propagation – Configuration system now propagates user_id, user_email, custom_data through multiple layers; ensure consistent threading throughout dynamic.py, user_config.py, and store.py.
  • Redis atomic operations – Flag updates must be atomic and correctly handle the new flags_with_targeting structure; verify _process_feature_targeting logic and cache eviction.
  • Sarvam provider selection logic – Multiple conditional branches for STT/TTS provider selection based on environment variables and voice overrides; ensure fallback behavior is correct.
  • Session lifecycle management – Separation of daily_aiohttp_session and proxy-aware session in app/main.py; verify both sessions are properly initialized and closed.
  • Background task integration – Langfuse task initialization timing and conditional startup only when tasks exist; ensure no orphaned tasks or lifecycle issues.

Possibly related PRs

Suggested reviewers

  • badri-singhal
  • murdore
  • swaroopvarma1

Poem

🐰 Flags now dance with user insight,
Bucketed fair, distributed right!
Sarvam voices, Soniox sounds,
AB tests across all grounds,
Redis whispers, webhooks gleam—
Feature flagging builds the dream! 🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Devcycle enhancements' is vague and generic, using non-descriptive phrasing that doesn't convey the specific nature of the changes. Replace with a more descriptive title that captures the main focus, such as 'Add Redis-backed DevCycle local evaluation with webhook support' or 'Implement user-aware AB testing and Sarvam provider integration'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 97.10% which is sufficient. The required threshold is 80.00%.

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.

Copy link

@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.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
.env.example (1)

182-212: Unresolved merge conflicts make the example file unusable.

The .env.example file contains multiple unresolved Git merge conflict markers (lines 182-188 and 196-212). Developers copying this file as a template will encounter parsing issues and confusion.

Resolve the conflicts by keeping the intended configuration. Based on the PR context, this likely means keeping all the new variables:

-<<<<<<< HEAD
 DEVCYCLE_SERVER_KEY=  # Your DevCycle server key for feature flag management
 DEVCYCLE_WEBHOOK_SECRET=  # Secret token for DevCycle webhook authentication (query param)
 
 
-=======
->>>>>>> 6906c02 (adding redis support)
 # Redis Configuration
 # Simple configuration - only these variables needed
 REDIS_HOST=localhost
 REDIS_PORT=6379
 
 # Optional: For Redis cluster setup
 REDIS_CLUSTER_NODES=redis1:6379,redis2:6380,redis3:6381
-<<<<<<< HEAD
 
 LANGFUSE_EVALUATORS="breeze buddy outcome correctness, latency evaluator" # name of the evaluator
 SLACK_WEBHOOK_URL="" # slack webhook url to send alerts
 SLACK_TAG_USERS="@john.doe,@jane.smith,@dev.team" # comma-separated list of users to tag in Slack alerts
 
 # Background Tasks Configuration
 ENABLE_BACKGROUND_TASKS=true  # Enable/disable the entire background task scheduler (default: true)
 BACKGROUND_TASKS_LOOP_INTERVAL_SECONDS=60  # How often the scheduler checks for tasks to run (in seconds, default: 60)
 
 # Langfuse Score Monitoring Configuration
 ENABLE_BB_LANGFUSE_MONITORING_LOOP=false  # Enable/disable Langfuse score monitoring task only (default: false)
 SCORE_CHECK_INTERVAL_SECONDS=600  # Check interval for Langfuse scores (10 minutes)
 
-DAILY_SUMMARY_HOUR = "21" # Hour of the day (0-23) to send daily summary alerts (default: 21 for 9 PM)
-=======
->>>>>>> 6906c02 (adding redis support)
+DAILY_SUMMARY_HOUR="21" # Hour of the day (0-23) to send daily summary alerts (default: 21 for 9 PM)

Note: Also fix DAILY_SUMMARY_HOUR = "21" to DAILY_SUMMARY_HOUR="21" (remove spaces around =) for proper .env syntax.

app/core/config/static.py (1)

480-509: Unresolved merge conflict will cause application startup failure.

The file contains Git merge conflict markers that produce Python syntax errors, as confirmed by static analysis (Ruff: invalid-syntax). The application will fail to import this module.

Apply this diff to resolve the conflict:

 REDIS_TTL = int(os.getenv("REDIS_TTL", "3600"))  # Default TTL in seconds (1 hour)
-<<<<<<< HEAD:app/core/config/static.py
 
 # DevCycle Configuration
 DEVCYCLE_WEBHOOK_SECRET = os.getenv("DEVCYCLE_WEBHOOK_SECRET", "")
 DEVCYCLE_SERVER_KEY = os.getenv("DEVCYCLE_SERVER_KEY", "")
 
 # Langfuse Score Monitoring Configuration
 LANGFUSE_EVALUATORS = [
     name.strip()
     for name in os.environ.get("LANGFUSE_EVALUATORS", "").split(",")
     if name.strip()
 ]
 
 # Slack Webhook Configuration
 SLACK_WEBHOOK_URL = os.environ.get("SLACK_WEBHOOK_URL", "")
 SLACK_TAG_USERS = os.environ.get("SLACK_TAG_USERS", "narsimha.reddy")
 
 BACKGROUND_TASKS_LOOP_INTERVAL_SECONDS = int(
     os.environ.get("BACKGROUND_TASKS_LOOP_INTERVAL_SECONDS", "60")
 )  # How often the scheduler checks tasks (in seconds)
 
 # Langfuse Score Monitoring Configuration
 ENABLE_BB_LANGFUSE_MONITORING_LOOP = (
     os.environ.get("ENABLE_BB_LANGFUSE_MONITORING_LOOP", "false").lower() == "true"
 )
 SCORE_CHECK_INTERVAL_SECONDS = int(
     os.environ.get("SCORE_CHECK_INTERVAL_SECONDS", "600")
 )  # 10 minutes
-=======
->>>>>>> 6906c02 (adding redis support):app/core/config.py
app/services/redis/client.py (3)

9-32: Critical: Unresolved git merge conflicts will cause syntax errors.

This file contains unresolved merge conflict markers (<<<<<<< HEAD, =======, >>>>>>>). The code will fail to parse and execute. These conflicts must be resolved before merging.

The conflicts appear throughout the file:

  • Lines 9-32: Import statements
  • Lines 72-148: _create_client method
  • Lines 191-218: close method
  • Lines 246-271: set method signature
  • Lines 322-344: incr/expire methods
  • Lines 372-377: close_redis_connections function

72-109: Resolve cluster initialization logic conflict.

The HEAD branch uses ClusterNode objects and checks REDIS_CLUSTER_NODES explicitly, while the other branch uses simple dict nodes and checks node count. The HEAD approach appears more intentional for cluster mode detection.

Recommended resolution - keep HEAD's explicit cluster mode detection:

# Determine if we should use cluster mode
# Use cluster if: explicitly set OR multiple nodes configured
use_cluster = len(startup_nodes) and REDIS_CLUSTER_NODES

if use_cluster:
    # CLUSTER MODE with ClusterNode objects
    ...
else:
    # SINGLE NODE MODE
    ...

246-271: Resolve set method signature conflict.

HEAD includes nx (set-if-not-exists) and ex (expiration) parameters which provide important functionality for distributed locking and cache management. The simpler branch loses this capability.

Recommended: Keep HEAD's version with nx and ex parameters for full Redis SET functionality.

app/ai/voice/agents/automatic/tts/__init__.py (1)

94-140: Critical: Additional merge conflict in Sarvam TTS service construction.

This section contains another unresolved merge conflict between two approaches:

  1. HEAD branch: Uses build_sarvam_tts(SarvamTTSConfig(...)) pattern
  2. Feature branch: Directly constructs SarvamTTSService with InputParams

Based on the relevant code snippets showing build_sarvam_tts exists in app/ai/voice/agents/automatic/tts/sarvam.py, you should resolve the conflict by choosing one consistent approach.

🧹 Nitpick comments (16)
app/core/config/static.py (1)

494-495: Consider using an empty default for SLACK_TAG_USERS.

The default value "narsimha.reddy" appears to be a developer-specific setting that may not be appropriate for all deployments.

-SLACK_TAG_USERS = os.environ.get("SLACK_TAG_USERS", "narsimha.reddy")
+SLACK_TAG_USERS = os.environ.get("SLACK_TAG_USERS", "")
docs/DEVCYCLE_ARCHITECTURE.md (3)

28-44: Add a language specifier to the fenced code block.

This ASCII diagram code block should have a language specifier for consistency. Consider using text or plaintext as the language identifier.

-```
+```text
 DevCycle Dashboard Change

341-363: Add a language specifier to the architecture diagram code block.

Same as above, add text or plaintext as the language identifier for the ASCII diagram.


506-508: Use proper Markdown link syntax for bare URLs.

Convert bare URLs to proper Markdown links for better rendering and accessibility.

Apply this diff:

-- DevCycle CDN: `https://config-cdn.devcycle.com/config/v1/server/{key}.json`
-- DevCycle Docs: https://docs.devcycle.com
-- Redis Sentinel: https://redis.io/topics/sentinel
-- Python Multiprocessing: https://docs.python.org/3/library/multiprocessing.html
+- DevCycle CDN: `https://config-cdn.devcycle.com/config/v1/server/{key}.json`
+- [DevCycle Docs](https://docs.devcycle.com)
+- [Redis Sentinel](https://redis.io/topics/sentinel)
+- [Python Multiprocessing](https://docs.python.org/3/library/multiprocessing.html)
app/agents/voice/automatic/stt/__init__.py (1)

104-110: Same defensive check recommended for translation terms.

Similar to general_items, add validation to avoid KeyError if source or target keys are missing.

         translation_terms_objects = None
         if translation_terms_items:
             translation_terms_objects = [
                 SonioxContextTranslationTerm(
                     source=item["source"], target=item["target"]
                 )
                 for item in translation_terms_items
+                if "source" in item and "target" in item
             ]
app/services/redis/client.py (1)

322-344: Keep incr and expire methods from HEAD.

The incr and expire methods are useful for rate limiting, counters, and TTL management. These should be retained in the final resolution.

DAILY_PROXY_FIX.md (1)

12-15: Add language specifier to the error log code block.

The error symptoms code block should specify a language for proper syntax highlighting. Use text or log as the identifier.

-```
+```text
 Time out joining https://juspay.daily.co/GAXJSKUVsCPEt7Xh4R5X
app/services/live_config/store.py (1)

459-467: Potential None value when key doesn't exist in flags_dict.

If normalized_key was never added to flags_dict earlier in the flow (e.g., the feature has targeting but no global distribution), flags_dict.get(normalized_key, {}).get("value") will return None, which may not be the intended behavior.

Consider whether a missing global value should be logged or handled differently.

     # Store targeting data for this feature (only if we have variations)
     if variation_values:
+        existing_value = flags_dict.get(normalized_key, {}).get("value")
+        if existing_value is None:
+            logger.debug(f"Targeting data for {normalized_key} has no global value")
         flags_dict[normalized_key] = {
-            "value": flags_dict.get(normalized_key, {}).get(
-                "value"
-            ),  # Preserve global value
+            "value": existing_value,  # Preserve global value (may be None)
             "has_targeting": True,
             "targets": targets,
             "variation_values": variation_values,
         }
app/examples/hybrid_ab_testing_examples.py (1)

64-68: Remove extraneous f prefix from strings without placeholders.

Multiple print statements use f-strings without any placeholders (lines 64, 98, 132, 159, 160, 197, 224, 255, 295, 335, 336, 365). While harmless, they should be regular strings for consistency.

Example fix:

-    print(f"Example 1: Voice Agent Integration")
+    print("Example 1: Voice Agent Integration")
app/core/config/user_config.py (3)

14-14: Wildcard import may re-export unintended symbols.

The from app.core.config.static import * imports all symbols, which may pollute the module namespace with unintended exports. If the intention is to re-export static config, consider explicit imports or documenting this behavior.


79-90: Use is for type comparisons instead of ==.

Type comparisons should use is rather than == for correctness and clarity. While == works for built-in types, is is the idiomatic approach.

         if user_value is not None:
             # Convert to expected type
-            if return_type == bool:
+            if return_type is bool:
                 return bool(user_value)
-            elif return_type == str:
+            elif return_type is str:
                 return str(user_value)
-            elif return_type == int:
+            elif return_type is int:
                 return int(user_value)
-            elif return_type == float:
+            elif return_type is float:
                 return float(user_value)
             else:
                 return user_value

85-88: Type conversion may raise ValueError for invalid inputs.

The direct type conversions (int(user_value), float(user_value)) can raise ValueError if the value cannot be converted (e.g., int("abc")). Consider wrapping in try/except or using the convert_type utility from store.py which handles this.

+from app.services.live_config.utils import convert_type
+
 # In get_config_for_user:
         if user_value is not None:
-            # Convert to expected type
-            if return_type is bool:
-                return bool(user_value)
-            elif return_type is str:
-                return str(user_value)
-            elif return_type is int:
-                return int(user_value)
-            elif return_type is float:
-                return float(user_value)
-            else:
-                return user_value
+            converted = convert_type(user_value, return_type)
+            if converted is not None:
+                return converted
+            # Fall through to global config if conversion fails
app/services/live_config/rule_evaluator.py (3)

24-26: Docstring inaccuracy: This is FIFO eviction, not LRU.

The comment says "LRU-like cache" but the implementation uses next(iter(_bucket_cache)) which removes the oldest-inserted key (FIFO), not the least-recently-used. For true LRU behavior, consider using functools.lru_cache or collections.OrderedDict with move-to-end on access.


352-406: Code duplication with get_variation_for_user.

evaluate_for_user (lines 380-406) duplicates the targeting/variation assignment logic from get_variation_for_user (lines 276-303). Consider refactoring to have evaluate_for_user call get_variation_for_user internally to reduce duplication.

 async def evaluate_for_user(
     feature_key: str,
     user_id: str,
     user_email: Optional[str] = None,
     custom_data: Optional[Dict[str, Any]] = None,
     default_value: Any = None,
 ) -> Any:
-    """
-    Evaluate feature flag value for a specific user.
-    Optimized to reuse cached flag data, avoiding duplicate Redis calls.
-    """
-    # Get all flags once (cached)
-    all_flags = await _get_all_flags_cached()
+    """Evaluate feature flag value for a specific user."""
+    all_flags = await _get_all_flags_from_redis()
     if not all_flags:
         return None

     flag_data = all_flags.get(feature_key)
     if not flag_data or not isinstance(flag_data, dict):
         return None

-    # Early return if no targeting
     if not flag_data.get("has_targeting"):
         return None

-    targets = flag_data.get("targets", [])
-    if not targets:
-        return None
-
-    # Inline variation assignment (avoid separate function call)
-    variation_id = None
-
-    # 1. Check targeting rules
-    for target in targets:
-        audience = target.get("audience")
-        if not audience:
-            continue
-
-        if user_matches_audience(audience, user_id, user_email, custom_data):
-            distribution = target.get("distribution", [])
-            if distribution:
-                variation_id = get_variation_from_distribution(
-                    distribution, user_id, feature_key
-                )
-                break
-
-    # 2. Fall back to default distribution
-    if not variation_id:
-        for target in targets:
-            if not target.get("audience"):
-                distribution = target.get("distribution", [])
-                if distribution:
-                    variation_id = get_variation_from_distribution(
-                        distribution, user_id, feature_key
-                    )
-                    break
+    variation_id = await get_variation_for_user(
+        feature_key, user_id, user_email, custom_data
+    )

     if not variation_id:
         return None

-    # Get value for variation (no additional Redis call)
     variation_values = flag_data.get("variation_values", {})
-    value = variation_values.get(variation_id, default_value)
-
-    logger.debug(
-        f"{feature_key} for user {user_email or user_id[:8]}: "
-        f"variation={variation_id}, value={value}"
-    )
-
-    return value
+    return variation_values.get(variation_id, default_value)

470-511: Multiple redundant Redis calls for the same data.

This debugging function makes at least 3 Redis calls: one in get_variation_for_user (line 481), one in get_value_for_user (line 485), and another direct call (line 495). Consider fetching flags once and reusing the data.

app/core/config/dynamic.py (1)

46-53: Inconsistencies with other config accessors.

  1. These functions lack user_id/user_email/custom_data parameters unlike all other accessors. If intentional (truly global settings), consider adding a comment explaining why.

  2. Line 48 uses string "false" as default while other bool functions use False directly (e.g., line 22, 64, 177). Use False for consistency.

 async def ENABLE_BACKGROUND_TASKS() -> bool:
     """Returns ENABLE_BACKGROUND_TASKS from Redis"""
-    return await get_config("ENABLE_BACKGROUND_TASKS", "false", bool)
+    return await get_config("ENABLE_BACKGROUND_TASKS", False, bool)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01aed6c and b948ef8.

📒 Files selected for processing (18)
  • .env.example (2 hunks)
  • DAILY_PROXY_FIX.md (1 hunks)
  • app/agents/voice/automatic/stt/__init__.py (1 hunks)
  • app/ai/voice/agents/automatic/__init__.py (1 hunks)
  • app/ai/voice/agents/automatic/tts/__init__.py (3 hunks)
  • app/ai/voice/agents/automatic/utils/common.py (1 hunks)
  • app/api/routers/devcycle.py (3 hunks)
  • app/core/config/dynamic.py (2 hunks)
  • app/core/config/static.py (2 hunks)
  • app/core/config/user_config.py (1 hunks)
  • app/core/logger/__init__.py (1 hunks)
  • app/examples/hybrid_ab_testing_examples.py (1 hunks)
  • app/main.py (3 hunks)
  • app/services/live_config/rule_evaluator.py (1 hunks)
  • app/services/live_config/store.py (6 hunks)
  • app/services/redis/client.py (13 hunks)
  • docs/DEVCYCLE_ARCHITECTURE.md (1 hunks)
  • requirements.txt (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-12T10:56:55.990Z
Learnt from: Swetha-160303
Repo: juspay/clairvoyance PR: 424
File: app/ai/voice/agents/automatic/tts/__init__.py:132-152
Timestamp: 2025-12-12T10:56:55.990Z
Learning: In app/ai/voice/agents/automatic/tts/__init__.py, FishAudioTTSService does not support the text_filters parameter, unlike ElevenLabsTTSService and GoogleTTSService which do support it.

Applied to files:

  • app/ai/voice/agents/automatic/tts/__init__.py
🧬 Code graph analysis (7)
app/ai/voice/agents/automatic/utils/common.py (1)
app/ai/voice/tts/sarvam.py (1)
  • get_sarvam_language (28-43)
app/ai/voice/agents/automatic/__init__.py (1)
app/core/config/dynamic.py (1)
  • ENABLE_BREEZE_MCP (56-69)
app/api/routers/devcycle.py (2)
app/services/redis/client.py (1)
  • get (236-244)
app/services/live_config/store.py (1)
  • fetch_and_update_feature_flags (85-222)
app/ai/voice/agents/automatic/tts/__init__.py (5)
app/ai/voice/agents/automatic/types/models.py (2)
  • TTSProvider (8-11)
  • VoiceName (14-17)
app/ai/voice/tts/elevenlabs.py (2)
  • ElevenLabsConfig (15-23)
  • build_elevenlabs_tts (26-40)
app/ai/voice/tts/google.py (2)
  • GoogleConfig (15-21)
  • build_google_tts (24-34)
app/ai/voice/agents/automatic/utils/common.py (2)
  • SarvamServiceType (9-13)
  • get_sarvam_language (16-55)
app/core/config/dynamic.py (5)
  • SARVAM_TTS_LANGUAGE_CODE (105-118)
  • SARVAM_TTS_MODEL (137-150)
  • SARVAM_TTS_VOICE_ID (153-166)
  • SARVAM_TTS_PITCH (201-214)
  • SARVAM_TTS_PACE (217-230)
app/agents/voice/automatic/stt/__init__.py (4)
app/ai/voice/agents/automatic/types/models.py (1)
  • VoiceName (14-17)
app/ai/voice/agents/automatic/utils/common.py (2)
  • SarvamServiceType (9-13)
  • get_sarvam_language (16-55)
app/core/config/dynamic.py (5)
  • SARVAM_STT_HIGH_VAD_SENSITIVITY (185-198)
  • SARVAM_STT_LANGUAGE_CODE (89-102)
  • SARVAM_STT_MODEL (73-86)
  • SARVAM_STT_PROMPT (121-134)
  • SARVAM_STT_VAD_SIGNALS (169-182)
app/services/redis/client.py (1)
  • get (236-244)
app/core/config/user_config.py (3)
app/services/live_config/rule_evaluator.py (2)
  • get_value_for_user (424-435)
  • is_feature_enabled (438-462)
app/services/live_config/store.py (1)
  • get_config (273-342)
app/core/config/dynamic.py (7)
  • BREEZE_MCP_ENDPOINT_PATH (30-43)
  • DAILY_SUMMARY_HOUR (51-53)
  • ENABLE_BREEZE_MCP (56-69)
  • ENABLE_FAL_SMART_TURN (14-27)
  • SARVAM_STT_MODEL (73-86)
  • SARVAM_TTS_MODEL (137-150)
  • SARVAM_TTS_VOICE_ID (153-166)
app/core/config/dynamic.py (1)
app/services/live_config/store.py (1)
  • get_config (273-342)
🪛 dotenv-linter (4.0.0)
.env.example

[warning] 182-182: [KeyWithoutValue] The <<<<<<< HEAD key should be with a value or have an equal sign

(KeyWithoutValue)


[warning] 182-182: [LeadingCharacter] Invalid leading character detected

(LeadingCharacter)


[warning] 183-183: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 183-183: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 184-184: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 184-184: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 186-186: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)


[warning] 187-187: [LeadingCharacter] Invalid leading character detected

(LeadingCharacter)


[warning] 188-188: [IncorrectDelimiter] The >>>>>>> 6906c02 (adding redis support) key has incorrect delimiter

(IncorrectDelimiter)


[warning] 188-188: [KeyWithoutValue] The >>>>>>> 6906c02 (adding redis support) key should be with a value or have an equal sign

(KeyWithoutValue)


[warning] 188-188: [LeadingCharacter] Invalid leading character detected

(LeadingCharacter)


[warning] 188-188: [LowercaseKey] The >>>>>>> 6906c02 (adding redis support) key should be in uppercase

(LowercaseKey)


[warning] 196-196: [DuplicatedKey] The <<<<<<< HEAD key is duplicated

(DuplicatedKey)


[warning] 196-196: [KeyWithoutValue] The <<<<<<< HEAD key should be with a value or have an equal sign

(KeyWithoutValue)


[warning] 196-196: [LeadingCharacter] Invalid leading character detected

(LeadingCharacter)


[warning] 196-196: [UnorderedKey] The <<<<<<< HEAD key should go before the REDIS_CLUSTER_NODES key

(UnorderedKey)


[warning] 211-211: [DuplicatedKey] The key is duplicated

(DuplicatedKey)


[warning] 211-211: [LeadingCharacter] Invalid leading character detected

(LeadingCharacter)


[warning] 211-211: [UnorderedKey] The key should go before the LANGFUSE_EVALUATORS key

(UnorderedKey)


[warning] 212-212: [DuplicatedKey] The >>>>>>> 6906c02 (adding redis support) key is duplicated

(DuplicatedKey)


[warning] 212-212: [IncorrectDelimiter] The >>>>>>> 6906c02 (adding redis support) key has incorrect delimiter

(IncorrectDelimiter)


[warning] 212-212: [KeyWithoutValue] The >>>>>>> 6906c02 (adding redis support) key should be with a value or have an equal sign

(KeyWithoutValue)


[warning] 212-212: [LeadingCharacter] Invalid leading character detected

(LeadingCharacter)


[warning] 212-212: [LowercaseKey] The >>>>>>> 6906c02 (adding redis support) key should be in uppercase

(LowercaseKey)


[warning] 212-212: [UnorderedKey] The >>>>>>> 6906c02 (adding redis support) key should go before the LANGFUSE_EVALUATORS key

(UnorderedKey)

🪛 LanguageTool
docs/DEVCYCLE_ARCHITECTURE.md

[grammar] ~204-~204: Ensure spelling is correct
Context: ...p. Why Rejected: - Latency: 50-100ms API call during voice session (unaccept...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~317-~317: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...s Sentinel/Cluster) 3. Config size: Very large configs (>10MB) may impact Redis perfor...

(EN_WEAK_ADJECTIVE)


[style] ~513-~513: Some style guides suggest that commas should set off the year in a month-day-year date.
Context: ...*: 1.0 Last Updated: December 14, 2024 Author: Clairvoyance Engineering ...

(MISSING_COMMA_AFTER_YEAR)

🪛 markdownlint-cli2 (0.18.1)
docs/DEVCYCLE_ARCHITECTURE.md

28-28: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


341-341: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


506-506: Bare URL used

(MD034, no-bare-urls)


507-507: Bare URL used

(MD034, no-bare-urls)


508-508: Bare URL used

(MD034, no-bare-urls)

DAILY_PROXY_FIX.md

12-12: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.14.8)
app/examples/hybrid_ab_testing_examples.py

64-64: f-string without any placeholders

Remove extraneous f prefix

(F541)


98-98: f-string without any placeholders

Remove extraneous f prefix

(F541)


132-132: f-string without any placeholders

Remove extraneous f prefix

(F541)


159-159: f-string without any placeholders

Remove extraneous f prefix

(F541)


160-160: f-string without any placeholders

Remove extraneous f prefix

(F541)


197-197: f-string without any placeholders

Remove extraneous f prefix

(F541)


224-224: f-string without any placeholders

Remove extraneous f prefix

(F541)


255-255: f-string without any placeholders

Remove extraneous f prefix

(F541)


295-295: f-string without any placeholders

Remove extraneous f prefix

(F541)


335-335: f-string without any placeholders

Remove extraneous f prefix

(F541)


336-336: f-string without any placeholders

Remove extraneous f prefix

(F541)


365-365: f-string without any placeholders

Remove extraneous f prefix

(F541)


413-413: Do not catch blind exception: Exception

(BLE001)

app/services/live_config/store.py

314-314: Do not catch blind exception: Exception

(BLE001)


371-371: Consider moving this statement to an else block

(TRY300)

app/services/live_config/rule_evaluator.py

510-510: Do not use bare except

(E722)


510-511: try-except-pass detected, consider logging the exception

(S110)

app/api/routers/devcycle.py

127-127: Do not catch blind exception: Exception

(BLE001)

app/core/logger/__init__.py

67-67: Expected a statement

(invalid-syntax)


67-67: Expected a statement

(invalid-syntax)


67-67: Expected a statement

(invalid-syntax)


67-67: Expected a statement

(invalid-syntax)


70-70: Expected a statement

(invalid-syntax)


70-70: Expected a statement

(invalid-syntax)


70-70: Expected a statement

(invalid-syntax)


70-70: Expected a statement

(invalid-syntax)


70-71: Expected a statement

(invalid-syntax)


72-72: Expected a statement

(invalid-syntax)


72-72: Expected a statement

(invalid-syntax)


72-72: Expected a statement

(invalid-syntax)


72-72: Expected a statement

(invalid-syntax)


72-72: Expected ,, found name

(invalid-syntax)


72-72: Expected ,, found name

(invalid-syntax)


72-72: Expected ,, found name

(invalid-syntax)


72-72: Expected ,, found name

(invalid-syntax)


72-72: Unparenthesized generator expression cannot be used here

(invalid-syntax)


72-72: Invalid assignment target

(invalid-syntax)


72-72: Expected in, found )

(invalid-syntax)

app/services/redis/client.py

9-9: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


23-23: Expected a statement

(invalid-syntax)


23-23: Expected a statement

(invalid-syntax)


23-23: Expected a statement

(invalid-syntax)


23-23: Expected a statement

(invalid-syntax)


23-24: Expected a statement

(invalid-syntax)


32-32: Expected a statement

(invalid-syntax)


32-32: Expected a statement

(invalid-syntax)


32-32: Expected a statement

(invalid-syntax)


32-32: Expected a statement

(invalid-syntax)


32-32: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


32-32: Expected ,, found name

(invalid-syntax)


32-32: Expected ,, found name

(invalid-syntax)


72-72: Expected except or finally after try block

(invalid-syntax)


72-72: Expected a statement

(invalid-syntax)


72-72: Expected a statement

(invalid-syntax)


72-72: Expected a statement

(invalid-syntax)


104-104: Expected a statement

(invalid-syntax)


104-104: Expected a statement

(invalid-syntax)


104-104: Expected a statement

(invalid-syntax)


104-104: Expected a statement

(invalid-syntax)


104-105: Expected a statement

(invalid-syntax)


106-106: Unexpected indentation

(invalid-syntax)


109-109: Expected a statement

(invalid-syntax)


109-109: Expected a statement

(invalid-syntax)


109-109: Expected a statement

(invalid-syntax)


109-109: Expected a statement

(invalid-syntax)


109-109: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


109-109: Expected ,, found name

(invalid-syntax)


109-109: Expected ,, found name

(invalid-syntax)


119-119: Expected a statement

(invalid-syntax)


119-119: Expected a statement

(invalid-syntax)


119-119: Expected a statement

(invalid-syntax)


119-119: Expected a statement

(invalid-syntax)


130-130: Expected a statement

(invalid-syntax)


130-130: Expected a statement

(invalid-syntax)


130-130: Expected a statement

(invalid-syntax)


130-130: Expected a statement

(invalid-syntax)


130-131: Expected a statement

(invalid-syntax)


131-131: Unexpected indentation

(invalid-syntax)


134-134: unindent does not match any outer indentation level

(invalid-syntax)


134-134: Expected a statement

(invalid-syntax)


134-134: Expected a statement

(invalid-syntax)


134-135: Expected a statement

(invalid-syntax)


135-135: Unexpected indentation

(invalid-syntax)


146-146: unindent does not match any outer indentation level

(invalid-syntax)


146-146: Expected a statement

(invalid-syntax)


146-146: Expected a statement

(invalid-syntax)


146-147: Expected an expression

(invalid-syntax)


147-147: Unexpected indentation

(invalid-syntax)


148-148: Expected a statement

(invalid-syntax)


148-148: Expected a statement

(invalid-syntax)


148-148: Expected a statement

(invalid-syntax)


148-148: Expected a statement

(invalid-syntax)


148-148: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


148-148: Expected ,, found name

(invalid-syntax)


148-148: Expected ,, found name

(invalid-syntax)


191-191: Expected a statement

(invalid-syntax)


191-191: Expected a statement

(invalid-syntax)


191-191: Expected a statement

(invalid-syntax)


191-191: Expected a statement

(invalid-syntax)


207-207: Expected a statement

(invalid-syntax)


207-207: Expected a statement

(invalid-syntax)


207-207: Expected a statement

(invalid-syntax)


207-207: Expected a statement

(invalid-syntax)


207-208: Expected a statement

(invalid-syntax)


208-208: Unexpected indentation

(invalid-syntax)


218-218: Expected a statement

(invalid-syntax)


218-218: Expected a statement

(invalid-syntax)


218-218: Expected a statement

(invalid-syntax)


218-218: Expected a statement

(invalid-syntax)


218-218: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


218-218: Expected ,, found name

(invalid-syntax)


218-218: Expected ,, found name

(invalid-syntax)


246-246: Expected a statement

(invalid-syntax)


246-246: Expected a statement

(invalid-syntax)


246-246: Expected a statement

(invalid-syntax)


246-246: Expected a statement

(invalid-syntax)


265-265: Expected except or finally after try block

(invalid-syntax)


265-265: Expected a statement

(invalid-syntax)


265-265: Expected a statement

(invalid-syntax)


265-265: Expected a statement

(invalid-syntax)


265-266: Expected a statement

(invalid-syntax)


266-266: Unexpected indentation

(invalid-syntax)


271-271: Expected except or finally after try block

(invalid-syntax)


271-271: Expected a statement

(invalid-syntax)


271-271: Expected a statement

(invalid-syntax)


271-271: Expected a statement

(invalid-syntax)


271-271: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


271-271: Expected ,, found name

(invalid-syntax)


271-271: Expected ,, found name

(invalid-syntax)


322-322: Expected a statement

(invalid-syntax)


322-322: Expected a statement

(invalid-syntax)


322-322: Expected a statement

(invalid-syntax)


322-322: Expected a statement

(invalid-syntax)


343-343: Expected a statement

(invalid-syntax)


343-343: Expected a statement

(invalid-syntax)


343-343: Expected a statement

(invalid-syntax)


343-343: Expected a statement

(invalid-syntax)


343-344: Expected a statement

(invalid-syntax)


344-344: Expected a statement

(invalid-syntax)


344-344: Expected a statement

(invalid-syntax)


344-344: Expected a statement

(invalid-syntax)


344-344: Expected a statement

(invalid-syntax)


344-344: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


344-344: Expected ,, found name

(invalid-syntax)


344-344: Expected ,, found name

(invalid-syntax)


372-372: Expected a statement

(invalid-syntax)


372-372: Expected a statement

(invalid-syntax)


372-372: Expected a statement

(invalid-syntax)


372-372: Expected a statement

(invalid-syntax)


373-373: Unexpected indentation

(invalid-syntax)


374-374: Expected a statement

(invalid-syntax)


374-374: Expected a statement

(invalid-syntax)


374-374: Expected a statement

(invalid-syntax)


374-374: Expected a statement

(invalid-syntax)


374-375: Expected a statement

(invalid-syntax)


375-375: Unexpected indentation

(invalid-syntax)


376-376: Expected a statement

(invalid-syntax)


376-376: Expected a statement

(invalid-syntax)


376-376: Expected a statement

(invalid-syntax)


376-376: Expected a statement

(invalid-syntax)


376-376: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


376-376: Expected ,, found name

(invalid-syntax)


376-376: Expected ,, found name

(invalid-syntax)

app/ai/voice/agents/automatic/tts/__init__.py

3-3: Expected a statement

(invalid-syntax)


3-3: Expected a statement

(invalid-syntax)


3-3: Expected a statement

(invalid-syntax)


3-3: Expected a statement

(invalid-syntax)


4-4: Expected a statement

(invalid-syntax)


4-4: Expected a statement

(invalid-syntax)


4-4: Expected a statement

(invalid-syntax)


4-4: Expected a statement

(invalid-syntax)


4-5: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Invalid annotated assignment target

(invalid-syntax)


9-9: Expected ,, found name

(invalid-syntax)


9-9: Expected ,, found name

(invalid-syntax)


9-9: Expected ,, found name

(invalid-syntax)


9-9: Expected ,, found name

(invalid-syntax)


9-9: Unparenthesized generator expression cannot be used here

(invalid-syntax)


9-9: Invalid assignment target

(invalid-syntax)


9-9: Expected in, found )

(invalid-syntax)


16-16: Expected a statement

(invalid-syntax)


16-16: Expected a statement

(invalid-syntax)


16-16: Expected a statement

(invalid-syntax)


16-16: Expected a statement

(invalid-syntax)


26-26: Expected ,, found :

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found .

(invalid-syntax)


27-27: Expected ,, found ==

(invalid-syntax)


27-27: Expected ,, found ==

(invalid-syntax)


27-27: Expected ,, found ==

(invalid-syntax)


27-27: Expected ,, found =

(invalid-syntax)


27-27: Expected an import name or a ')'

(invalid-syntax)


28-28: Expected ,, found >>

(invalid-syntax)


28-28: Expected ,, found >>

(invalid-syntax)


28-28: Expected ,, found >>

(invalid-syntax)


28-28: Expected ,, found >

(invalid-syntax)


28-28: Expected an import name or a ')'

(invalid-syntax)


30-30: Expected ,, found and

(invalid-syntax)


30-30: Expected an import name or a ')'

(invalid-syntax)


31-31: Expected ,, found .

(invalid-syntax)


31-31: Expected ,, found .

(invalid-syntax)


31-31: Expected ,, found .

(invalid-syntax)


31-31: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


36-37: Expected ), found newline

(invalid-syntax)


37-37: Unexpected indentation

(invalid-syntax)


37-37: Expected an expression

(invalid-syntax)


37-38: Expected in, found newline

(invalid-syntax)


38-38: Expected an indented block after for statement

(invalid-syntax)


38-38: Expected a module name

(invalid-syntax)


38-39: Expected one or more symbol names after import

(invalid-syntax)


40-40: Expected an import name

(invalid-syntax)


40-41: Expected one or more symbol names after import

(invalid-syntax)


94-94: Expected a statement

(invalid-syntax)


94-94: Expected a statement

(invalid-syntax)


94-94: Expected a statement

(invalid-syntax)


94-94: Expected a statement

(invalid-syntax)


95-95: Expected a statement

(invalid-syntax)


95-95: Expected a statement

(invalid-syntax)


95-95: Expected a statement

(invalid-syntax)


95-95: Expected a statement

(invalid-syntax)


95-96: Expected a statement

(invalid-syntax)


97-97: Expected a statement

(invalid-syntax)


97-97: Expected a statement

(invalid-syntax)


97-97: Expected a statement

(invalid-syntax)


97-97: Expected a statement

(invalid-syntax)


97-97: Invalid annotated assignment target

(invalid-syntax)


97-97: Expected ,, found name

(invalid-syntax)


97-97: Expected ,, found name

(invalid-syntax)


97-97: Expected ,, found name

(invalid-syntax)


97-97: Expected ,, found name

(invalid-syntax)


97-97: Unparenthesized generator expression cannot be used here

(invalid-syntax)


97-97: Invalid assignment target

(invalid-syntax)


97-97: Expected in, found )

(invalid-syntax)


98-98: Unexpected indentation

(invalid-syntax)


104-104: Expected a statement

(invalid-syntax)


104-104: Expected a statement

(invalid-syntax)


104-104: Expected a statement

(invalid-syntax)


104-104: Expected a statement

(invalid-syntax)


116-116: Expected a statement

(invalid-syntax)


116-116: Expected a statement

(invalid-syntax)


116-116: Expected a statement

(invalid-syntax)


116-116: Expected a statement

(invalid-syntax)


116-117: Expected a statement

(invalid-syntax)


118-118: Unexpected indentation

(invalid-syntax)


140-140: Expected a statement

(invalid-syntax)


140-140: Expected a statement

(invalid-syntax)


140-140: Expected a statement

(invalid-syntax)


140-140: Expected a statement

(invalid-syntax)


140-140: Invalid annotated assignment target

(invalid-syntax)


140-140: Expected ,, found name

(invalid-syntax)


140-140: Expected ,, found name

(invalid-syntax)


140-140: Expected ,, found name

(invalid-syntax)


140-140: Expected ,, found name

(invalid-syntax)


140-140: Unparenthesized generator expression cannot be used here

(invalid-syntax)


140-140: Invalid assignment target

(invalid-syntax)


140-140: Expected in, found )

(invalid-syntax)

app/main.py

65-65: Expected a statement

(invalid-syntax)


65-65: Expected a statement

(invalid-syntax)


65-65: Expected a statement

(invalid-syntax)


65-65: Expected a statement

(invalid-syntax)


68-68: Expected a statement

(invalid-syntax)


68-68: Expected a statement

(invalid-syntax)


68-68: Expected a statement

(invalid-syntax)


68-68: Expected a statement

(invalid-syntax)


68-69: Expected a statement

(invalid-syntax)


69-69: Expected a statement

(invalid-syntax)


69-69: Expected a statement

(invalid-syntax)


69-69: Expected a statement

(invalid-syntax)


69-69: Expected a statement

(invalid-syntax)


69-69: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


69-69: Expected ,, found name

(invalid-syntax)


69-69: Expected ,, found name

(invalid-syntax)


106-106: Expected except or finally after try block

(invalid-syntax)


106-106: Expected a statement

(invalid-syntax)


106-106: Expected a statement

(invalid-syntax)


106-106: Expected a statement

(invalid-syntax)


107-107: Unexpected indentation

(invalid-syntax)


108-108: Expected a statement

(invalid-syntax)


108-108: Expected a statement

(invalid-syntax)


108-108: Expected a statement

(invalid-syntax)


108-108: Expected a statement

(invalid-syntax)


108-109: Expected a statement

(invalid-syntax)


109-109: Unexpected indentation

(invalid-syntax)


110-110: Expected a statement

(invalid-syntax)


110-110: Expected a statement

(invalid-syntax)


110-110: Expected a statement

(invalid-syntax)


110-110: Expected a statement

(invalid-syntax)


110-110: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


110-110: Expected ,, found name

(invalid-syntax)


110-110: Expected ,, found name

(invalid-syntax)


111-111: Unexpected indentation

(invalid-syntax)


112-112: unindent does not match any outer indentation level

(invalid-syntax)


112-112: Expected a statement

(invalid-syntax)


112-112: Expected a statement

(invalid-syntax)


112-113: Expected a statement

(invalid-syntax)


113-113: Unexpected indentation

(invalid-syntax)


114-114: unindent does not match any outer indentation level

(invalid-syntax)


114-114: Expected a statement

(invalid-syntax)


114-114: Expected a statement

(invalid-syntax)


114-115: Expected an expression

(invalid-syntax)


115-115: Unexpected indentation

(invalid-syntax)


117-117: Expected a statement

(invalid-syntax)


117-117: Expected a statement

(invalid-syntax)


117-117: Expected a statement

(invalid-syntax)


117-117: Expected a statement

(invalid-syntax)


120-120: Unexpected indentation

(invalid-syntax)


124-124: Expected a statement

(invalid-syntax)


124-124: Expected a statement

(invalid-syntax)


124-124: Expected a statement

(invalid-syntax)


124-124: Expected a statement

(invalid-syntax)


124-125: Expected a statement

(invalid-syntax)


125-125: Expected a statement

(invalid-syntax)


125-125: Expected a statement

(invalid-syntax)


125-125: Expected a statement

(invalid-syntax)


125-125: Expected a statement

(invalid-syntax)


125-125: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


125-125: Expected ,, found name

(invalid-syntax)


125-125: Expected ,, found name

(invalid-syntax)


127-127: Unexpected indentation

(invalid-syntax)

app/agents/voice/automatic/stt/__init__.py

127-127: Consider moving this statement to an else block

(TRY300)


129-129: Do not catch blind exception: Exception

(BLE001)


146-148: Avoid specifying long messages outside the exception class

(TRY003)


165-165: Avoid specifying long messages outside the exception class

(TRY003)


210-212: Avoid specifying long messages outside the exception class

(TRY003)


223-225: Avoid specifying long messages outside the exception class

(TRY003)


240-240: Avoid specifying long messages outside the exception class

(TRY003)


273-273: Avoid specifying long messages outside the exception class

(TRY003)

app/core/config/static.py

480-480: Expected a statement

(invalid-syntax)


480-480: Expected a statement

(invalid-syntax)


480-480: Expected a statement

(invalid-syntax)


480-480: Expected a statement

(invalid-syntax)


508-508: Expected a statement

(invalid-syntax)


508-508: Expected a statement

(invalid-syntax)


508-508: Expected a statement

(invalid-syntax)


508-508: Expected a statement

(invalid-syntax)


508-509: Expected a statement

(invalid-syntax)


509-509: Expected a statement

(invalid-syntax)


509-509: Expected a statement

(invalid-syntax)


509-509: Expected a statement

(invalid-syntax)


509-509: Expected a statement

(invalid-syntax)


509-509: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


509-509: Invalid annotated assignment target

(invalid-syntax)


509-509: Expected ,, found name

(invalid-syntax)


509-509: Expected ,, found name

(invalid-syntax)

app/core/config/user_config.py

14-14: from app.core.config.static import * used; unable to detect undefined names

(F403)


81-81: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)


83-83: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)


85-85: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)


87-87: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)


92-92: Do not catch blind exception: Exception

(BLE001)


233-250: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

🔇 Additional comments (20)
app/ai/voice/agents/automatic/__init__.py (1)

279-296: LGTM! User-context-aware feature flag evaluation.

The priority-based user identification (email → name → session_id) provides good stickiness for AB testing while ensuring a fallback for anonymous users. The custom_data payload correctly includes relevant targeting attributes.

app/ai/voice/agents/automatic/utils/common.py (1)

9-13: LGTM! Clean enum definition for service type differentiation.

The SarvamServiceType enum cleanly separates TTS and STT concerns, enabling the function to apply appropriate fallback strategies for each service type.

app/agents/voice/automatic/stt/__init__.py (1)

309-317: LGTM - Good fallback to Google STT.

The default case properly logs the choice and returns a configured Google STT service with sensible defaults.

app/api/routers/devcycle.py (4)

24-42: LGTM - Timing-safe webhook secret verification.

Good use of hmac.compare_digest to prevent timing attacks on the secret comparison. The logging of misconfiguration is also helpful for debugging.


34-36: Consider fail-closed behavior when webhook secret is not configured.

Currently, if DEVCYCLE_WEBHOOK_SECRET is not set, the function returns False, which correctly rejects the request. However, this means the webhook endpoint is effectively disabled until the secret is configured.

Verify this is the intended behavior. If webhooks should work without authentication in development environments, consider adding an explicit configuration option like DEVCYCLE_WEBHOOK_AUTH_ENABLED.


75-94: Good observability improvements.

The addition of start_time for timing, logging of changed_key and event_type, and inclusion of [PRESENT]/[MISSING] in auth failure logs significantly improves debugging capability.


127-140: Broad exception catch is acceptable here.

While Ruff flags this as BLE001, this is a top-level error handler in an HTTP endpoint where catching all exceptions and returning a structured error response is appropriate. The exception is logged with context, and a proper HTTP response is returned.

app/services/live_config/store.py (4)

115-127: Good optimization with pre-built variation lookup table.

The O(1) variation lookup via variation_lookup dict is a nice performance improvement over nested loops.


190-193: Early return optimization may have unexpected behavior with nested dicts.

The comparison old_flags == new_flags_with_targeting performs a deep equality check on potentially large nested dictionaries. While this works correctly, be aware that:

  1. Dict comparison can be expensive for large flag sets
  2. Order-sensitive comparisons in targets lists might cause false negatives

This is acceptable for the optimization goal but worth noting.


298-317: User-level evaluation with graceful fallback looks good.

The try/except pattern with fallback to global config is appropriate for resilience. The broad Exception catch (line 314) is acceptable here since we're implementing a fallback pattern and logging the warning.


366-371: Backward compatibility handling for flag structure is correct.

The logic correctly handles both the new dict-with-value format and the legacy direct-value format.

app/examples/hybrid_ab_testing_examples.py (1)

390-421: Example runner with comprehensive coverage looks good.

The main() function demonstrates all the hybrid AB testing scenarios clearly. The broad exception handler is appropriate for a demo script to show the error gracefully.

app/core/config/user_config.py (2)

107-132: User-level feature flag wrappers are well-designed.

The convenience wrappers like ENABLE_BREEZE_MCP_FOR_USER provide a clean API that mirrors the global config pattern while adding user context. Good API design.


221-230: Backward compatibility imports are intentionally placed at bottom.

The imports at the bottom avoid circular import issues while maintaining backward compatibility. This pattern is acceptable.

app/services/live_config/rule_evaluator.py (3)

76-165: LGTM!

The targeting condition matchers handle null checks properly and support the expected operators consistently.


168-222: LGTM!

The audience matching with short-circuit evaluation for AND/OR operators is well-implemented and performant.


307-344: LGTM!

The distribution logic with cumulative percentages and fallback handling is correct.

app/core/config/dynamic.py (3)

1-11: LGTM!

Clear documentation explaining the optional user context parameters for AB testing support.


14-43: LGTM!

Consistent pattern for user-context-aware config accessors.


56-246: LGTM!

All Sarvam config accessors and ENABLE_TRACING follow the consistent user-context-aware pattern correctly.

Comment on lines +96 to +100
if general_items:
general_objects = [
SonioxContextGeneralItem(key=item["key"], value=item["value"])
for item in general_items
]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential KeyError masked by broad exception catch.

If general_items contains dictionaries missing "key" or "value" keys, a KeyError will be raised but caught by the broad except Exception on line 129. Consider validating the structure or catching specific exceptions.

         general_objects = None
         if general_items:
-            general_objects = [
-                SonioxContextGeneralItem(key=item["key"], value=item["value"])
-                for item in general_items
-            ]
+            general_objects = [
+                SonioxContextGeneralItem(key=item["key"], value=item["value"])
+                for item in general_items
+                if "key" in item and "value" in item
+            ]

Comment on lines +168 to +172
sarvam_stt_model = await SARVAM_STT_MODEL()
sarvam_stt_language_code = await SARVAM_STT_LANGUAGE_CODE()
sarvam_stt_prompt = await SARVAM_STT_PROMPT()
sarvam_stt_vad_signals = await SARVAM_STT_VAD_SIGNALS()
sarvam_stt_high_vad_sensitivity = await SARVAM_STT_HIGH_VAD_SENSITIVITY()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

User context not passed to dynamic config functions.

The SARVAM_STT_MODEL() and related functions accept user_id, user_email, and custom_data parameters for user-level targeting (as shown in the relevant code snippets), but they're called without arguments here. This bypasses any user-specific A/B testing or targeting configured in DevCycle.

Consider accepting user context in get_stt_service and passing it through:

-async def get_stt_service(voice_name: Optional[str] = None):
+async def get_stt_service(
+    voice_name: Optional[str] = None,
+    user_id: Optional[str] = None,
+    user_email: Optional[str] = None,
+    custom_data: Optional[Dict[str, Any]] = None,
+):
     ...
-        sarvam_stt_model = await SARVAM_STT_MODEL()
-        sarvam_stt_language_code = await SARVAM_STT_LANGUAGE_CODE()
-        sarvam_stt_prompt = await SARVAM_STT_PROMPT()
-        sarvam_stt_vad_signals = await SARVAM_STT_VAD_SIGNALS()
-        sarvam_stt_high_vad_sensitivity = await SARVAM_STT_HIGH_VAD_SENSITIVITY()
+        sarvam_stt_model = await SARVAM_STT_MODEL(user_id, user_email, custom_data)
+        sarvam_stt_language_code = await SARVAM_STT_LANGUAGE_CODE(user_id, user_email, custom_data)
+        sarvam_stt_prompt = await SARVAM_STT_PROMPT(user_id, user_email, custom_data)
+        sarvam_stt_vad_signals = await SARVAM_STT_VAD_SIGNALS(user_id, user_email, custom_data)
+        sarvam_stt_high_vad_sensitivity = await SARVAM_STT_HIGH_VAD_SENSITIVITY(user_id, user_email, custom_data)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/agents/voice/automatic/stt/__init__.py around lines 168–172 the dynamic
config functions SARVAM_STT_MODEL, SARVAM_STT_LANGUAGE_CODE, SARVAM_STT_PROMPT,
SARVAM_STT_VAD_SIGNALS, and SARVAM_STT_HIGH_VAD_SENSITIVITY are being invoked
without user context, so user-level targeting/A-B rules are bypassed; update the
get_stt_service signature to accept optional user context (user_id, user_email,
custom_data), thread those parameters through to every call site of
get_stt_service, and pass them into each dynamic config call (e.g.,
SARVAM_STT_MODEL(user_id, user_email, custom_data)) so DevCycle receives the
user context; ensure defaulting when context is absent and update any
callers/tests accordingly.

Comment on lines +277 to +279
if SONIOX_LANGUAGE_HINTS:
lang_list = [lang.strip() for lang in SONIOX_LANGUAGE_HINTS.split(",")]
language_hints = [Language(lang) for lang in lang_list if lang]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unhandled ValueError when parsing invalid language hints.

Language(lang) will raise ValueError if lang is not a valid enum value. Since this parses user-provided environment variable content, invalid values could crash the service initialization.

Apply this diff to handle invalid language hints gracefully:

         language_hints = None
         if SONIOX_LANGUAGE_HINTS:
             lang_list = [lang.strip() for lang in SONIOX_LANGUAGE_HINTS.split(",")]
-            language_hints = [Language(lang) for lang in lang_list if lang]
+            language_hints = []
+            for lang in lang_list:
+                if lang:
+                    try:
+                        language_hints.append(Language(lang))
+                    except ValueError:
+                        logger.warning(f"Invalid Soniox language hint: {lang}, skipping")
+            language_hints = language_hints if language_hints else None

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/agents/voice/automatic/stt/__init__.py around lines 277 to 279, parsing
SONIOX_LANGUAGE_HINTS uses Language(lang) which raises ValueError for invalid
enum values; update the code to catch and ignore invalid entries (optionally
logging a warning) so a malformed environment variable doesn't crash
initialization—iterate the split language list, try to construct Language(lang)
inside a try/except ValueError block, append only successful conversions to
language_hints, and skip or log invalid items.

Comment on lines 3 to 46
<<<<<<< HEAD:app/ai/voice/agents/automatic/tts/__init__.py
=======
from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
from pipecat.services.google.tts import GoogleTTSService
from pipecat.services.sarvam.tts import SarvamTTSService

>>>>>>> d5f9259 (Integration of the sarvam ai for stt and tts):app/agents/voice/automatic/tts/__init__.py
from pipecat.transcriptions.language import Language

from app.ai.voice.agents.automatic.features.charts.highlight_filter import (
HighlightedChartTextFilter,
)

<<<<<<< HEAD:app/ai/voice/agents/automatic/tts/__init__.py
from app.ai.voice.agents.automatic.types import TTSProvider, VoiceName
from app.ai.voice.tts import (
ElevenLabsConfig,
GoogleConfig,
Integration,
SarvamServiceType,
SarvamTTSConfig,
TTSProvider,
VoiceName,
:app/agents/voice/automatic/tts/__init__.py,
=======,
>>>>>>>,
ai,
and,
app.agents.voice.automatic.types,
app.agents.voice.automatic.utils.common,
build_elevenlabs_tts,
build_google_tts,
build_sarvam_tts,
d5f9259,
for,
from,
get_sarvam_language,
import,
of,
sarvam,
stt,
the,
tts,
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Unresolved git merge conflict markers break this file.

This file contains multiple unresolved merge conflict markers (<<<<<<< HEAD, =======, >>>>>>>) that make it completely unparsable. The imports section (lines 3-46) and the Sarvam TTS logic (lines 94-140) are corrupted.

The file will fail to import with syntax errors. Please resolve the merge conflicts before proceeding.

🧰 Tools
🪛 Ruff (0.14.8)

3-3: Expected a statement

(invalid-syntax)


3-3: Expected a statement

(invalid-syntax)


3-3: Expected a statement

(invalid-syntax)


3-3: Expected a statement

(invalid-syntax)


4-4: Expected a statement

(invalid-syntax)


4-4: Expected a statement

(invalid-syntax)


4-4: Expected a statement

(invalid-syntax)


4-4: Expected a statement

(invalid-syntax)


4-5: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Expected a statement

(invalid-syntax)


9-9: Invalid annotated assignment target

(invalid-syntax)


9-9: Expected ,, found name

(invalid-syntax)


9-9: Expected ,, found name

(invalid-syntax)


9-9: Expected ,, found name

(invalid-syntax)


9-9: Expected ,, found name

(invalid-syntax)


9-9: Unparenthesized generator expression cannot be used here

(invalid-syntax)


9-9: Invalid assignment target

(invalid-syntax)


9-9: Expected in, found )

(invalid-syntax)


16-16: Expected a statement

(invalid-syntax)


16-16: Expected a statement

(invalid-syntax)


16-16: Expected a statement

(invalid-syntax)


16-16: Expected a statement

(invalid-syntax)


26-26: Expected ,, found :

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found /

(invalid-syntax)


26-26: Expected ,, found .

(invalid-syntax)


27-27: Expected ,, found ==

(invalid-syntax)


27-27: Expected ,, found ==

(invalid-syntax)


27-27: Expected ,, found ==

(invalid-syntax)


27-27: Expected ,, found =

(invalid-syntax)


27-27: Expected an import name or a ')'

(invalid-syntax)


28-28: Expected ,, found >>

(invalid-syntax)


28-28: Expected ,, found >>

(invalid-syntax)


28-28: Expected ,, found >>

(invalid-syntax)


28-28: Expected ,, found >

(invalid-syntax)


28-28: Expected an import name or a ')'

(invalid-syntax)


30-30: Expected ,, found and

(invalid-syntax)


30-30: Expected an import name or a ')'

(invalid-syntax)


31-31: Expected ,, found .

(invalid-syntax)


31-31: Expected ,, found .

(invalid-syntax)


31-31: Expected ,, found .

(invalid-syntax)


31-31: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


32-32: Expected ,, found .

(invalid-syntax)


36-37: Expected ), found newline

(invalid-syntax)


37-37: Unexpected indentation

(invalid-syntax)


37-37: Expected an expression

(invalid-syntax)


37-38: Expected in, found newline

(invalid-syntax)


38-38: Expected an indented block after for statement

(invalid-syntax)


38-38: Expected a module name

(invalid-syntax)


38-39: Expected one or more symbol names after import

(invalid-syntax)


40-40: Expected an import name

(invalid-syntax)


40-41: Expected one or more symbol names after import

(invalid-syntax)


46-46: Expected a statement

(invalid-syntax)

🤖 Prompt for AI Agents
In app/ai/voice/agents/automatic/tts/__init__.py around lines 3-46 (and also
address corrupted logic around lines 94-140), there are unresolved Git merge
conflict markers (<<<<<<<, =======, >>>>>>>) that make the file unparsable;
remove all conflict markers, choose and keep the correct import lines (merge the
needed TTS service imports: ElevenLabsTTSService, GoogleTTSService,
SarvamTTSService and the correct Language, HighlightedChartTextFilter,
TTSProvider, VoiceName and config/build function imports) so there are no
duplicate or garbled tokens, and restore the Sarvam TTS logic block (lines
~94-140) to a single coherent implementation (remove leftover branch labels and
stray text), then run lint/type checks and unit tests to verify the module
imports and Sarvam integration load without syntax errors.

Comment on lines +16 to +55
def get_sarvam_language(
language_code: Optional[str],
service_type: SarvamServiceType,
) -> Optional[Language]:
"""
Convert SARVAM language code to Language enum with appropriate fallbacks.

Args:
language_code: Language code string (e.g., "en-IN", "hi-IN")
service_type: Service type (TTS or STT) to determine fallback behavior

Returns:
- For TTS: Language enum value, fallback to EN_IN if invalid
- For STT: Language enum value, or None if invalid/not provided
"""
if language_code:
try:
return Language(language_code)
except ValueError:
# Different fallback behavior for TTS vs STT
if service_type == SarvamServiceType.TTS:
logger.warning(
f"Invalid TTS language code: {language_code}, falling back to EN_IN"
)
return Language.EN_IN
else:
logger.warning(
f"Invalid STT language code: {language_code}, returning None"
)
return None
else:
# No language code provided
if service_type == SarvamServiceType.TTS:
logger.warning(
"No SARVAM TTS language code provided, falling back to EN_IN"
)
return Language.EN_IN
else:
logger.debug("No SARVAM STT language code provided, returning None")
return None
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "get_sarvam_language" --type=py -B2 -A5

Repository: juspay/clairvoyance

Length of output: 7457


🏁 Script executed:

cat -n app/ai/voice/tts/sarvam.py | head -45

Repository: juspay/clairvoyance

Length of output: 1504


🏁 Script executed:

cat -n app/ai/voice/stt/sarvam.py | head -65

Repository: juspay/clairvoyance

Length of output: 2284


🏁 Script executed:

cat -n app/ai/voice/agents/automatic/utils/common.py | head -60

Repository: juspay/clairvoyance

Length of output: 2198


🏁 Script executed:

# Check what imports the old implementations to understand consolidation scope
rg -n "from app\.ai\.voice\.tts.*import.*get_sarvam_language|from app\.ai\.voice\.stt.*import.*get_sarvam_language" --type=py

Repository: juspay/clairvoyance

Length of output: 45


🏁 Script executed:

# Check internal usage in tts module - see line 49 in sarvam.py
grep -n "get_sarvam_language" app/ai/voice/tts/sarvam.py

Repository: juspay/clairvoyance

Length of output: 268


🏁 Script executed:

# Check internal usage in stt module - see line 80 in sarvam.py
grep -n "get_sarvam_language" app/ai/voice/stt/sarvam.py

Repository: juspay/clairvoyance

Length of output: 256


🏁 Script executed:

# Check if old functions are still being imported elsewhere
rg "from app\.ai\.voice\.(tts|stt)\.sarvam import" --type=py

Repository: juspay/clairvoyance

Length of output: 45


Three separate implementations of get_sarvam_language exist—consolidation was not completed.

Beyond the duplication with app/ai/voice/tts/sarvam.py, there is also an identical implementation in app/ai/voice/stt/sarvam.py (line 38). Both old implementations remain actively used internally:

  • app/ai/voice/tts/sarvam.py line 49: calls its own get_sarvam_language
  • app/ai/voice/stt/sarvam.py line 80: calls its own get_sarvam_language

The new unified version in common.py adds service-type-specific behavior but does not replace the existing implementations, leaving two separate function bodies with duplicated logic. Update build_sarvam_tts and build_sarvam_stt to use the unified get_sarvam_language from common.py and remove the duplicate implementations from their respective modules.

Comment on lines +230 to +246
async def _get_all_flags_cached() -> Dict[str, Any]:
"""
Get all flags from Redis with caching to reduce repeated calls.
Cache is cleared on each new request to ensure fresh data.
"""
redis = await get_redis_service()
client = await redis.get_client()

raw = await client.get(REDIS_KEY_FLAGS)
if not raw:
return {}

try:
return json.loads(raw)
except json.JSONDecodeError:
logger.error("Invalid JSON in flags data")
return {}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Misleading function name: _get_all_flags_cached performs no caching.

The function name and docstring claim caching, but every call fetches fresh data from Redis. If this is intentional (relying on Redis as the cache), rename to _get_all_flags_from_redis. Otherwise, implement actual in-memory caching with TTL.

-async def _get_all_flags_cached() -> Dict[str, Any]:
+async def _get_all_flags_from_redis() -> Dict[str, Any]:
     """
-    Get all flags from Redis with caching to reduce repeated calls.
-    Cache is cleared on each new request to ensure fresh data.
+    Get all flags from Redis.
+    Redis serves as the cache layer; no additional in-memory caching.
     """
🤖 Prompt for AI Agents
In app/services/live_config/rule_evaluator.py around lines 230-246, the function
`_get_all_flags_cached` and its docstring claim caching but always reads from
Redis; either rename it to `_get_all_flags_from_redis` and update the docstring
to reflect it simply returns flags from Redis, or implement real in-process
caching: add a module-level cache structure (e.g., cached_flags and cached_at)
plus a TTL constant, protect refresh with an asyncio.Lock, on call return
cached_flags if now - cached_at < TTL otherwise fetch from Redis, update
cached_flags and cached_at, and keep JSON decode/error handling; pick one
approach and apply consistently to name, docstring and behavior.

Comment on lines +510 to +511
except:
pass
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid bare except: pass — silently swallowing errors hinders debugging.

For a debugging function like get_user_assignment_info, silently ignoring exceptions is counterproductive. At minimum, log the error.

-    except:
-        pass
+    except Exception as e:
+        logger.warning(f"Failed to check targeting match for {feature_key}: {e}")

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.8)

510-510: Do not use bare except

(E722)


510-511: try-except-pass detected, consider logging the exception

(S110)

🤖 Prompt for AI Agents
In app/services/live_config/rule_evaluator.py around lines 510-511 there is a
bare "except: pass" that silently swallows errors; replace it with a specific
Exception handler that logs the error (preferably using logging.exception or the
module/class logger to include stack trace and context like function name and
user id) so failures in get_user_assignment_info are visible; keep swallowing
the exception only if intended, otherwise consider re-raising after logging.

Comment on lines +71 to +73
## Files Modified
- `app/main.py`: Separated Daily.co and general HTTP client sessions

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incomplete "Files Modified" section.

The documentation describes changes to both app/main.py and app/agents/voice/automatic/__init__.py (as mentioned in sections 1 and 2 of the Solution), but only app/main.py is listed here.

Apply this diff:

 ## Files Modified
 - `app/main.py`: Separated Daily.co and general HTTP client sessions
+- `app/agents/voice/automatic/__init__.py`: Removed proxy configuration for Daily WebRTC transport
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## Files Modified
- `app/main.py`: Separated Daily.co and general HTTP client sessions
## Files Modified
- `app/main.py`: Separated Daily.co and general HTTP client sessions
- `app/agents/voice/automatic/__init__.py`: Removed proxy configuration for Daily WebRTC transport
🤖 Prompt for AI Agents
In DAILY_PROXY_FIX.md around lines 71 to 73, the "Files Modified" list is
incomplete: it only shows `app/main.py` but the change also touched
`app/agents/voice/automatic/__init__.py`; update the section to include both
files (e.g. add a bullet for `app/agents/voice/automatic/__init__.py`) using the
same formatting/backticks as the existing entry and keep ordering consistent
with the Solution sections.

Comment on lines +512 to +514
**Document Version**: 1.0
**Last Updated**: December 14, 2024
**Author**: Clairvoyance Engineering Team
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect year in the "Last Updated" date.

The document states "December 14, 2024" but the current date is December 2025. This appears to be a typo.

Apply this diff:

-**Last Updated**: December 14, 2024  
+**Last Updated**: December 14, 2025  
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
**Document Version**: 1.0
**Last Updated**: December 14, 2024
**Author**: Clairvoyance Engineering Team
**Document Version**: 1.0
**Last Updated**: December 14, 2025
**Author**: Clairvoyance Engineering Team
🧰 Tools
🪛 LanguageTool

[style] ~513-~513: Some style guides suggest that commas should set off the year in a month-day-year date.
Context: ...*: 1.0 Last Updated: December 14, 2024 Author: Clairvoyance Engineering ...

(MISSING_COMMA_AFTER_YEAR)

🤖 Prompt for AI Agents
In docs/DEVCYCLE_ARCHITECTURE.md around lines 512 to 514 the "Last Updated" date
shows "December 14, 2024" which is incorrect; update that line to "December 14,
2025" (or the correct current year/date) so the document header reflects the
accurate last updated date, and save the file.

requirements.txt Outdated
Comment on lines +52 to +57
<<<<<<< HEAD

# DevCycle Feature Flag SDK
devcycle-python-server-sdk>=3.0.0
=======
>>>>>>> 6906c02 (adding redis support)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Unresolved merge conflict will break dependency installation.

The file contains Git merge conflict markers that will cause pip install -r requirements.txt to fail. These markers must be resolved before merging.

Apply this diff to resolve the conflict (assuming you want to keep the DevCycle SDK):

-<<<<<<< HEAD
 
 # DevCycle Feature Flag SDK
 devcycle-python-server-sdk>=3.0.0
-=======
->>>>>>> 6906c02 (adding redis support)
🤖 Prompt for AI Agents
In requirements.txt around lines 52 to 57 there are Git conflict markers
(<<<<<<<, =======, >>>>>>>) that will break pip; remove the conflict markers and
the redundant section, keeping the desired dependency line
"devcycle-python-server-sdk>=3.0.0" (or the chosen version), and ensure the file
has a single clean entry for the DevCycle SDK with no surrounding merge markers
so pip can install dependencies normally.

@priyanshi-2003 priyanshi-2003 force-pushed the devcycle-enhancements branch 3 times, most recently from 3680ba6 to 7e95be5 Compare December 14, 2025 18:27
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