Skip to content

feat(config): add feature flags for runtime endpoint gating#127

Open
SudipSinha wants to merge 4 commits into
mainfrom
feature-flags-metrics
Open

feat(config): add feature flags for runtime endpoint gating#127
SudipSinha wants to merge 4 commits into
mainfrom
feature-flags-metrics

Conversation

@SudipSinha
Copy link
Copy Markdown
Member

@SudipSinha SudipSinha commented Apr 22, 2026

Summary

  • Migrate from always-on endpoints to a feature flag system that mirrors Java's @EndpointDisabled pattern
  • Each metric group (fairness, drift, explainer) has a master gate plus individual metric flags
  • Flags can be overridden at deployment time via TRUSTYAI_ENABLE_<NAME> environment variables
  • New helpers: register_if_enabled() for single-gate endpoints, register_if_enabled_with_group() for group-gated metrics
  • Explainer endpoints now use the group gate like drift and fairness do
  • Fixes pyrefly unbound-name error for lm_evaluation_harness_router

Jira: RHOAIENG-55917

Test plan

  • uv run pytest tests/service/config/ -v — 15 tests pass
  • uv run pytest tests/test_app_integration.py -v — 13 tests pass
  • uv run ruff check src tests — lint clean
  • uv run pyrefly check — type check clean
  • Verify endpoints register/skip correctly with TRUSTYAI_ENABLE_* env vars

🤖 Generated with Claude Code

Summary by Sourcery

Introduce a feature-flag-based system for runtime endpoint gating and apply it to fairness, drift, explainer, and legacy metric routes.

New Features:

  • Add centralized endpoint feature flags with environment-variable overrides for controlling API route registration.
  • Gate fairness, drift, and explainer metric routers behind group and per-metric flags, including support for a new Jensen-Shannon drift metric endpoint.

Bug Fixes:

  • Avoid an unbound-name failure when the lm_evaluation_harness router import is unavailable.

Enhancements:

  • Centralize conditional router registration via helper functions to keep disabled endpoints out of the FastAPI registry and OpenAPI docs.

Tests:

  • Add unit tests for the feature flag configuration and environment overrides, as well as for the router registration helpers.

Summary by Sourcery

Introduce configurable feature flags to gate runtime registration of metrics and explainer endpoints, and add helpers to centralize router registration based on these flags.

New Features:

  • Add a centralized feature flag configuration for metrics and explainer endpoints with environment-variable overrides.
  • Introduce helper functions to conditionally register routers, including support for group-level and per-metric flags controlling endpoint exposure.

Enhancements:

  • Refactor main application router setup to use feature-flag-aware registration for fairness, drift, explainer, and legacy fairness endpoints, keeping disabled routes out of OpenAPI.
  • Add FastAPI integration tests to verify that disabling group or per-metric flags removes the corresponding OpenAPI paths.

Tests:

  • Add unit tests for feature flag parsing and environment overrides, and for router registration helpers across single and grouped flags.

Summary by CodeRabbit

  • New Features

    • Added environment variable-based feature flags to enable/disable API endpoint groups (fairness, drift, and explainer metrics) with sensible defaults.
    • Endpoints can now be conditionally registered based on feature flag configuration, allowing flexible endpoint exposure.
  • Tests

    • Added comprehensive test coverage for feature flag configuration and conditional endpoint registration logic.

@SudipSinha SudipSinha self-assigned this Apr 22, 2026
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Apr 22, 2026

Reviewer's Guide

Introduce centralized feature-flag-based endpoint gating for fairness, drift, and explainer routers, with deployment-time env overrides and helper functions that keep disabled routers out of the FastAPI app and OpenAPI docs, plus tests and a small lm_evaluation_harness import handling tweak.

Sequence diagram for endpoint registration with feature flags

sequenceDiagram
    actor Operator
    participant Env as EnvVars
    participant FeatureFlags as FeatureFlagsModule
    participant Registry as RegistryModule
    participant App as FastAPIApp
    participant FairnessRouter as Fairness_dir_router

    Operator->>Env: Configure TRUSTYAI_ENABLE_FAIRNESS, TRUSTYAI_ENABLE_FAIRNESS_DIR

    Env->>FeatureFlags: Access TRUSTYAI_ENABLE_FAIRNESS
    FeatureFlags->>FeatureFlags: _flag(name=fairness, default=true)
    FeatureFlags-->>Env: bool for fairness

    Env->>FeatureFlags: Access TRUSTYAI_ENABLE_FAIRNESS_DIR
    FeatureFlags->>FeatureFlags: _flag(name=fairness_dir, default=true)
    FeatureFlags-->>Env: bool for fairness_dir

    FeatureFlags->>FeatureFlags: Build ENDPOINTS dict

    App->>Registry: register_if_enabled_with_group(app, FairnessRouter, fairness, fairness_dir, tag)

    Registry->>FeatureFlags: _is_enabled(flag=fairness)
    FeatureFlags-->>Registry: group_enabled
    alt fairness group disabled
        Registry-->>App: Skip registration (log debug)
    else fairness group enabled
        Registry->>FeatureFlags: _is_enabled(flag=fairness_dir)
        FeatureFlags-->>Registry: metric_enabled
        alt fairness_dir metric disabled
            Registry-->>App: Skip registration (log debug)
        else fairness_dir metric enabled
            Registry->>App: _include_router(app, FairnessRouter, tag, prefix)
            App-->>Registry: Router included in app and OpenAPI
        end
    end
Loading

Class diagram for feature flag configuration and router registration helpers

classDiagram
    class FeatureFlagsModule {
        <<module>>
        -logging logger
        -frozenset _TRUTHY
        -frozenset _FALSY
        +bool _flag(name, default)
        +dict~str, bool~ ENDPOINTS
    }

    class RegistryModule {
        <<module>>
        -logging logger
        +bool _is_enabled(flag)
        +_include_router(app, router, tag, prefix)
        +register_if_enabled(app, router, flag, tag, prefix)
        +register_if_enabled_with_group(app, router, group_flag, metric_flag, tag, prefix)
    }

    class FastAPIApp {
        +include_router(router, tags, prefix)
    }

    class APIRouter {
        <<FastAPI.APIRouter>>
    }

    class MainModule {
        <<module>>
        +lifespan(_app)
        +fairness routers
        +drift routers
        +explainer routers
    }

    FeatureFlagsModule "1" --> "1" RegistryModule : provides ENDPOINTS used_by
    RegistryModule --> FastAPIApp : calls include_router
    RegistryModule --> APIRouter : conditionally registers routers
    MainModule --> RegistryModule : uses register_if_enabled
    MainModule --> RegistryModule : uses register_if_enabled_with_group
    MainModule --> FeatureFlagsModule : imports ENDPOINTS via RegistryModule
    MainModule --> FastAPIApp : configures app
    MainModule --> APIRouter : defines metric routers
Loading

File-Level Changes

Change Details Files
Add feature flag configuration for endpoint gating with environment-variable overrides.
  • Introduce src/service/config/feature_flags.py defining ENDPOINTS and flag() to derive boolean flags from TRUSTYAI_ENABLE env vars with robust truthy/falsy parsing and logging on invalid values.
  • Define explicit flag set for fairness, drift, and explainer groups and per-metric flags, including default-off explainer flags.
  • Add package init file for src/service/config and tests/service/config.
src/service/config/feature_flags.py
src/service/config/__init__.py
tests/service/config/__init__.py
tests/service/config/test_feature_flags.py
Centralize conditional router registration via helper functions and apply them in main application setup.
  • Add src/service/config/registry.py with _is_enabled(), _include_router(), register_if_enabled(), and register_if_enabled_with_group() to gate APIRouter registration based on ENDPOINTS.
  • Refactor src/main.py to replace direct include_router() calls for fairness, drift, explainer, and legacy fairness endpoints with register_if_enabled_with_group() calls, wiring appropriate group and metric flags, tags, and prefixes.
  • Leave lm_evaluation_harness_router import failure silently ignored by narrowing behavior to a bare pass, avoiding unbound-name failures when optional deps are missing.
src/service/config/registry.py
src/main.py
pyproject.toml
Add tests to validate feature flag behavior and router registration/openapi gating.
  • Extend tests/test_app_integration.py with a minimal FastAPI app builder that patches registry.ENDPOINTS and verifies disabling group or metric flags removes corresponding paths from OpenAPI.
  • Add tests/service/config/test_registry.py to cover register_if_enabled() and register_if_enabled_with_group() behavior, including combinations of group and metric flags, tag/prefix propagation, and explainer-specific gating.
  • Ensure all ENDPOINTS values are boolean and the key set matches expectations in tests/service/config/test_feature_flags.py.
tests/test_app_integration.py
tests/service/config/test_registry.py
tests/service/config/test_feature_flags.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@SudipSinha SudipSinha changed the title feat(config): add feature flags for runtime endpoint gating [RHOAIENG-55917] feat(config): add feature flags for runtime endpoint gating Apr 22, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

Review Change Stack

Warning

Rate limit exceeded

@SudipSinha has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 41 minutes and 3 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 715c7ae6-1f37-4435-9e62-ffb47d65dca2

📥 Commits

Reviewing files that changed from the base of the PR and between 987cb18 and 346bde4.

📒 Files selected for processing (4)
  • src/service/config/feature_flags.py
  • src/service/config/registry.py
  • tests/service/config/test_feature_flags.py
  • tests/test_app_integration.py
📝 Walkthrough

Walkthrough

This PR implements a feature-flag system for conditionally gating API endpoints in a FastAPI service. It introduces environment-driven configuration via the TRUSTYAI_ENABLE_* convention, adds router registration helpers that respect flag states, refactors the main application to use gated registration for fairness, drift, and explainer endpoints, and includes comprehensive unit and integration tests to verify the gating behavior.

Changes

Endpoint Gating via Feature Flags

Layer / File(s) Summary
Feature Flag Specification
src/service/config/__init__.py, src/service/config/feature_flags.py
Defines ENDPOINTS dictionary mapping endpoint group names (fairness, drift, explainer) and specific metrics to boolean flags, with environment override via TRUSTYAI_ENABLE_<FLAG_NAME> convention. Fairness and drift default to enabled; explainer defaults to disabled.
Flag Parsing Implementation
src/service/config/feature_flags.py
Implements _flag(name, default) helper that reads environment variables, normalizes string representations of boolean values (truthy: true/1/yes/on/enabled; falsy: false/0/no/off/disabled), logs warnings for unrecognized values, and falls back to defaults.
Router Registration Helpers
src/service/config/registry.py
Adds _is_enabled() to check flag state, _include_router() to register routers with optional tags and prefix, register_if_enabled() for single-flag gating, and register_if_enabled_with_group() for group+metric dual-flag gating with conditional logging.
Main Application Integration
src/main.py
Adds jensen_shannon drift router import and refactors fairness, drift, and explainer endpoint registrations from direct app.include_router() calls to register_if_enabled_with_group() calls, including deprecated legacy endpoints under /metrics.
Unit Tests
tests/service/config/test_feature_flags.py, tests/service/config/test_registry.py, tests/service/config/__init__.py
Validates ENDPOINTS structure and flag types; tests _flag() environment parsing across truthy/falsy variants, case insensitivity, whitespace trimming, unrecognized values, and missing vars; verifies router inclusion/skipping based on flag state and correct forwarding of tags and prefix.
Integration Tests
tests/test_app_integration.py
Constructs minimal FastAPI apps with overridden feature flags via _build_app_with_flags() and asserts that disabling fairness, drift, or metric-specific flags removes corresponding endpoint paths from /openapi.json.
Linter Configuration
pyproject.toml
Adds Ruff per-file ignore for PLR0913 (too many arguments) in src/service/config/registry.py.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • trustyai-explainability/trustyai-service#145: The registry helpers and gated router registration infrastructure enable conditional wiring of streaming KS test and other metric-specific routers by group and metric flag.

Poem

🐰 Flags now flutter where endpoints dwell,
Environment whispers—which routes to tell,
Fairness and drift dance, explainers hush,
Groups gating metrics, no API rush! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main change: introducing a feature flag system for runtime endpoint gating, which is the primary objective reflected throughout the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature-flags-metrics

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.

SudipSinha and others added 3 commits May 7, 2026 22:03
…-55917]

Migrate from always-on endpoints to a feature flag system that mirrors
Java's @EndpointDisabled pattern. Each metric group (fairness, drift,
explainer) has a master gate plus individual metric flags that can be
overridden at deployment time via TRUSTYAI_ENABLE_<NAME> env vars.

New helpers: register_if_enabled() for single-gate endpoints and
register_if_enabled_with_group() for group-gated metrics. Explainer
endpoints now use the group gate like drift and fairness do.

Also fixes pyrefly unbound-name error for lm_evaluation_harness_router.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
- Fix ruff lint violations (FBT001/FBT003, D104, ERA001, D417, ANN201, D101/D102)
- Make _flag() default parameter keyword-only
- Add missing __init__.py with docstrings for new packages
- Add return type annotations and docstrings to all test methods
- Add PLR0913 per-file-ignore for registry.py (6-param group-gated API)
- Replace dict[str, Any] with dict[str, str | list[str]] in registry.py
- Use ruff-formatted imports consistent with main branch

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Remove unused `data_download` flag (dead code, no router wired)
- Extract `_is_enabled` and `_include_router` helpers in registry.py to
  eliminate duplication between `register_if_enabled` and `_with_group`
- Add integration tests verifying disabled flags remove endpoints from
  OpenAPI: group-level (fairness, drift) and individual metric gating

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In registry._is_enabled, silently treating unknown flag names as False could hide configuration or typo issues; consider logging a warning (or even raising in debug modes) when a flag key is not present in ENDPOINTS so misconfigurations are surfaced early.
  • The ENDPOINTS dict and its keys are now a central contract used across main.py, registry, and tests; you might want to centralize these flag names (e.g., constants or an Enum) to avoid stringly-typed usage and reduce the risk of typos when adding new endpoints.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `registry._is_enabled`, silently treating unknown flag names as `False` could hide configuration or typo issues; consider logging a warning (or even raising in debug modes) when a flag key is not present in `ENDPOINTS` so misconfigurations are surfaced early.
- The `ENDPOINTS` dict and its keys are now a central contract used across `main.py`, `registry`, and tests; you might want to centralize these flag names (e.g., constants or an Enum) to avoid stringly-typed usage and reduce the risk of typos when adding new endpoints.

## Individual Comments

### Comment 1
<location path="src/service/config/feature_flags.py" line_range="35-44" />
<code_context>
+    return default
+
+
+ENDPOINTS: dict[str, bool] = {
+    "fairness": _flag("fairness", default=True),
+    "fairness_spd": _flag("fairness_spd", default=True),
+    "fairness_dir": _flag("fairness_dir", default=True),
+    "drift": _flag("drift", default=True),
+    "drift_ks_test": _flag("drift_ks_test", default=True),
+    "drift_jensen_shannon": _flag("drift_jensen_shannon", default=True),
+    "drift_compare_means": _flag("drift_compare_means", default=True),
+    "explainer": _flag("explainer", default=False),
+    "explainer_local": _flag("explainer_local", default=False),
+    "explainer_global": _flag("explainer_global", default=False),
+}
</code_context>
<issue_to_address>
**question (bug_risk):** Clarify or reconsider the default-disabled behavior for explainer endpoints relative to other groups.

Fairness and drift endpoints default to enabled, while explainer endpoints default to disabled. If that asymmetry is intentional (e.g., explainer endpoints are experimental/expensive), consider documenting it near these flags or in higher-level config to avoid operator confusion. If it’s not intentional, aligning explainer defaults to `True` would keep existing behavior and prevent explainer endpoints from disappearing on upgrade.
</issue_to_address>

### Comment 2
<location path="tests/service/config/test_feature_flags.py" line_range="9" />
<code_context>
+from src.service.config.feature_flags import ENDPOINTS, _flag
+
+
+class TestEndpointFlags:
+    """Tests for the ENDPOINTS dictionary defaults."""
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add explicit tests for default values of each endpoint flag (e.g., fairness/drift True, explainer False)

Current tests only assert that the flags are boolean and that the keys match, but not the specific default values. Given the documented contract (fairness/drift default to True; explainer, explainer_local, explainer_global default to False), please add a test that asserts these values in `ENDPOINTS` when no env overrides are set, so we can detect regressions in default endpoint exposure.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/service/config/feature_flags.py
from src.service.config.feature_flags import ENDPOINTS, _flag


class TestEndpointFlags:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Add explicit tests for default values of each endpoint flag (e.g., fairness/drift True, explainer False)

Current tests only assert that the flags are boolean and that the keys match, but not the specific default values. Given the documented contract (fairness/drift default to True; explainer, explainer_local, explainer_global default to False), please add a test that asserts these values in ENDPOINTS when no env overrides are set, so we can detect regressions in default endpoint exposure.

Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (4)
src/service/config/feature_flags.py (1)

35-46: 💤 Low value

ENDPOINTS is evaluated once at import time.

Because _flag(...) calls run at module import, changes to TRUSTYAI_ENABLE_* after the process has started have no effect, and tests that need to override flags must patch.dict ENDPOINTS directly (as the registry tests do) rather than mutate os.environ. This is the standard pattern, but worth a one-line note in the module docstring so consumers don't expect runtime reloading.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/service/config/feature_flags.py` around lines 35 - 46, ENDPOINTS is
populated by calling _flag(...) at import time so any changes to
TRUSTYAI_ENABLE_* after startup won't take effect; update the module docstring
to add a one-line note that ENDPOINTS (and its _flag evaluations) are evaluated
once at import and environment flag changes during runtime will not be picked
up, and reference ENDPOINTS, _flag, and the TRUSTYAI_ENABLE_* environment
variables so callers/tests know to patch ENDPOINTS directly if they need to
override flags at runtime.
src/service/config/registry.py (2)

12-13: ⚡ Quick win

Silent-default on missing flag keys can hide typos.

_is_enabled returns False for any flag not present in ENDPOINTS, so a typo in the flag/group_flag/metric_flag argument silently disables the route at startup with no error. Since the ENDPOINTS keys are a fixed, small set, consider raising or at least logger.warning(...) when an unknown key is queried, to catch typos early.

🛡️ Suggested change
 def _is_enabled(flag: str) -> bool:
-    return ENDPOINTS.get(flag, False)
+    if flag not in ENDPOINTS:
+        logger.warning("Unknown feature flag '%s' queried; treating as disabled", flag)
+        return False
+    return ENDPOINTS[flag]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/service/config/registry.py` around lines 12 - 13, _is_enabled currently
returns False for unknown keys which hides typos; update the function (def
_is_enabled) to first check if flag is in ENDPOINTS and if not either raise a
clear exception (ValueError with the missing flag and list(ENDPOINTS.keys())) to
fail fast at startup or at minimum call logger.warning(...) mentioning the
unknown flag and available keys before returning False—modify whichever behavior
your project prefers and ensure the message includes the flag and ENDPOINTS keys
for easy debugging.

16-27: 💤 Low value

# type: ignore could be avoided by passing kwargs explicitly.

The dict[str, str | list[str]] typing forces a # type: ignore[arg-type] because include_router expects more specific types per kwarg. Calling include_router with explicit named arguments removes the ignore and is clearer.

♻️ Optional refactor
 def _include_router(
     app: FastAPI,
     router: APIRouter,
     tag: str | None = None,
     prefix: str | None = None,
 ) -> None:
-    kwargs: dict[str, str | list[str]] = {}
-    if tag:
-        kwargs["tags"] = [tag]
-    if prefix:
-        kwargs["prefix"] = prefix
-    app.include_router(router, **kwargs)  # type: ignore[arg-type]
+    if tag and prefix:
+        app.include_router(router, tags=[tag], prefix=prefix)
+    elif tag:
+        app.include_router(router, tags=[tag])
+    elif prefix:
+        app.include_router(router, prefix=prefix)
+    else:
+        app.include_router(router)

Note: the existing tests assert exact kwargs (tags=.../prefix=.../none), so this remains compatible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/service/config/registry.py` around lines 16 - 27, The _include_router
function uses a generic kwargs dict and then calls app.include_router(... ) with
type: ignore; instead, change _include_router to call app.include_router with
explicit named args: pass tags=[tag] only when tag is not None and pass
prefix=prefix only when prefix is not None (omit each argument when its value is
None) so the call signature matches FastAPI's expected types and removes the
need for "# type: ignore[arg-type]" (refer to the _include_router function and
the app.include_router invocation to locate the change).
pyproject.toml (1)

111-111: 💤 Low value

Per-file PLR0913 ignore is reasonable but consider keyword-only params as an alternative.

register_if_enabled_with_group has 6 parameters, exceeding the default PLR0913 limit. As an alternative to the per-file ignore, you could mark the optional parameters keyword-only with *, (which improves call-site readability) — though this still wouldn't satisfy PLR0913's count. The current per-file ignore is acceptable; flagging only as a stylistic option.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyproject.toml` at line 111, The function register_if_enabled_with_group in
registry.py currently has six positional parameters triggering PLR0913; to
improve call-site readability, change the signature to make the optional
parameters keyword-only by inserting a lone * before the optional args (e.g.,
def register_if_enabled_with_group(required1, required2, *, optional1=None,
optional2=None, ...)), then update all call sites to pass those parameters by
name; note this won't reduce the PLR0913 count, so you may keep the existing
per-file "PLR0913" ignore if desired.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_app_integration.py`:
- Around line 313-316: The current test collects routes by substring matching
(using "/dir" and "/spd") which is brittle; update the logic in
tests/test_app_integration.py around the fairness_paths computation so it checks
only explicit fairness routes or a strict prefix (e.g., ensure no path starts
with "/fairness/" or compare against a known set of forbidden fairness paths)
instead of using broad substrings; locate the fairness_paths variable and
replace the list comprehension with a filter that either checks
path.startswith("/fairness/") or membership in a defined list of exact paths,
then assert that resulting list is empty.

---

Nitpick comments:
In `@pyproject.toml`:
- Line 111: The function register_if_enabled_with_group in registry.py currently
has six positional parameters triggering PLR0913; to improve call-site
readability, change the signature to make the optional parameters keyword-only
by inserting a lone * before the optional args (e.g., def
register_if_enabled_with_group(required1, required2, *, optional1=None,
optional2=None, ...)), then update all call sites to pass those parameters by
name; note this won't reduce the PLR0913 count, so you may keep the existing
per-file "PLR0913" ignore if desired.

In `@src/service/config/feature_flags.py`:
- Around line 35-46: ENDPOINTS is populated by calling _flag(...) at import time
so any changes to TRUSTYAI_ENABLE_* after startup won't take effect; update the
module docstring to add a one-line note that ENDPOINTS (and its _flag
evaluations) are evaluated once at import and environment flag changes during
runtime will not be picked up, and reference ENDPOINTS, _flag, and the
TRUSTYAI_ENABLE_* environment variables so callers/tests know to patch ENDPOINTS
directly if they need to override flags at runtime.

In `@src/service/config/registry.py`:
- Around line 12-13: _is_enabled currently returns False for unknown keys which
hides typos; update the function (def _is_enabled) to first check if flag is in
ENDPOINTS and if not either raise a clear exception (ValueError with the missing
flag and list(ENDPOINTS.keys())) to fail fast at startup or at minimum call
logger.warning(...) mentioning the unknown flag and available keys before
returning False—modify whichever behavior your project prefers and ensure the
message includes the flag and ENDPOINTS keys for easy debugging.
- Around line 16-27: The _include_router function uses a generic kwargs dict and
then calls app.include_router(... ) with type: ignore; instead, change
_include_router to call app.include_router with explicit named args: pass
tags=[tag] only when tag is not None and pass prefix=prefix only when prefix is
not None (omit each argument when its value is None) so the call signature
matches FastAPI's expected types and removes the need for "# type:
ignore[arg-type]" (refer to the _include_router function and the
app.include_router invocation to locate the change).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01863a9f-4198-4fc7-ad7c-cfdea21324ff

📥 Commits

Reviewing files that changed from the base of the PR and between ccc9091 and 987cb18.

📒 Files selected for processing (9)
  • pyproject.toml
  • src/main.py
  • src/service/config/__init__.py
  • src/service/config/feature_flags.py
  • src/service/config/registry.py
  • tests/service/config/__init__.py
  • tests/service/config/test_feature_flags.py
  • tests/service/config/test_registry.py
  • tests/test_app_integration.py

Comment on lines +313 to +316
fairness_paths = [
p for p in paths if "/fairness/" in p or "/dir" in p or "/spd" in p
]
assert fairness_paths == []
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use explicit fairness path checks instead of broad substring matching.

The "/dir" / "/spd" substring filter is brittle and can accidentally match unrelated future routes. Prefer asserting exact known fairness paths (or a strict fairness prefix) are absent.

Suggested tightening
-        fairness_paths = [
-            p for p in paths if "/fairness/" in p or "/dir" in p or "/spd" in p
-        ]
-        assert fairness_paths == []
+        expected_fairness_paths = {
+            "/metrics/fairness/group/dir",
+            "/metrics/fairness/group/dir/definition",
+            "/metrics/fairness/group/dir/request",
+            "/metrics/fairness/group/dir/requests",
+            "/metrics/fairness/group/spd",
+            "/metrics/fairness/group/spd/definition",
+            "/metrics/fairness/group/spd/request",
+            "/metrics/fairness/group/spd/requests",
+        }
+        assert expected_fairness_paths.isdisjoint(paths)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_app_integration.py` around lines 313 - 316, The current test
collects routes by substring matching (using "/dir" and "/spd") which is
brittle; update the logic in tests/test_app_integration.py around the
fairness_paths computation so it checks only explicit fairness routes or a
strict prefix (e.g., ensure no path starts with "/fairness/" or compare against
a known set of forbidden fairness paths) instead of using broad substrings;
locate the fairness_paths variable and replace the list comprehension with a
filter that either checks path.startswith("/fairness/") or membership in a
defined list of exact paths, then assert that resulting list is empty.

@SudipSinha SudipSinha added the ok-to-test Proceed with CI testing label May 7, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

PR image build and manifest generation completed successfully!

📦 PR image: quay.io/trustyai/trustyai-service-python-ci:346bde43d90ffa25d5eed07ad36c972ae3c51ca9

🗂️ CI manifests

devFlags:
  manifests:
    - contextDir: config
      sourcePath: ''
      uri: https://api.github.com/repos/trustyai-explainability/trustyai-service-operator-ci/tarball/service-python-346bde43d90ffa25d5eed07ad36c972ae3c51ca9

- Log warning on unknown feature flag queries instead of silent default
- Refactor _include_router to use explicit args, removing type:ignore
- Document explainer default-disabled rationale and import-time eval
- Add tests asserting specific default values for each flag group
- Tighten fairness path assertions with startswith instead of substring

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ok-to-test Proceed with CI testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant