Skip to content

feat: add provider/group binding for request filters#484

Merged
ding113 merged 5 commits intoding113:devfrom
miraserver:feat/req-filters-adv
Dec 30, 2025
Merged

feat: add provider/group binding for request filters#484
ding113 merged 5 commits intoding113:devfrom
miraserver:feat/req-filters-adv

Conversation

@miraserver
Copy link
Contributor

@miraserver miraserver commented Dec 29, 2025

Summary

This PR adds the ability to bind request filters to specific providers or provider groups, enabling more granular control over request/response manipulation.

Features

  • Provider binding: Filters can be bound to specific provider IDs and will only apply when requests are routed to those providers
  • Group binding: Filters can be bound to provider group tags, applying to all providers within a group
  • Global filters: Existing behavior preserved - filters with global binding apply to all requests
  • Two-phase application: applyGlobal() runs before provider selection, applyForProvider() runs after

Database Changes

  • Added binding_type column (global, providers, groups) to request_filters table
  • Added provider_ids (integer array) for provider binding
  • Added group_tags (text array) for group binding
  • Migration: drizzle/0041_sticky_jackal.sql

UI Improvements

  • New multi-select components for providers and groups in filter dialog
  • Improved table layout with truncation and tooltips for long values
  • Better dialog scrolling and layout

Performance Optimizations

  • Pre-compiled regex caching for text_replace filters
  • Set-based O(1) lookups for provider/group matching
  • Early exit when no filters configured
  • Skip tag parsing when no group-based filters exist

Files Changed

Category Files
Schema src/drizzle/schema.ts, drizzle/0041_sticky_jackal.sql
Engine src/lib/request-filter-engine.ts
Repository src/repository/request-filters.ts
Actions src/actions/request-filters.ts
Proxy src/app/v1/_lib/proxy/guard-pipeline.ts, provider-request-filter.ts, request-filter.ts
UI filter-dialog.tsx, filter-table.tsx, provider-multi-select.tsx, group-multi-select.tsx
i18n messages/{en,ja,ru,zh-CN,zh-TW}/settings.json
Tests tests/unit/request-filter-binding.test.ts (28 new tests)

Test plan

  • All 224 tests pass
  • Lint and typecheck pass
  • Manual test: Create global filter, verify applies to all requests
  • Manual test: Create provider-bound filter, verify applies only to selected provider
  • Manual test: Create group-bound filter, verify applies to providers with matching tag
  • Manual test: Verify filter dialog UI for provider/group selection

🤖 Generated with Claude Code

Greptile Summary

This PR extends the request filter system with granular provider and group binding capabilities, enabling filters to target specific providers or provider groups rather than applying globally.

Key Changes:

  • Two-phase filter application: Global filters run before provider selection (applyGlobal()), provider/group filters run after (applyForProvider())
  • Database schema: Added binding_type, provider_ids, group_tags columns with indexed lookups
  • Performance optimizations: Pre-compiled regex caching, Set-based O(1) provider/group matching, early exits when no filters configured
  • New pipeline step: providerRequestFilter guard executes after provider selection in both CHAT and COUNT_TOKENS pipelines
  • Comprehensive validation: Enforces mutual exclusivity between providers and groups, requires at least one selection for non-global bindings
  • UI enhancements: Multi-select components for providers/groups, improved table layout with truncation and tooltips
  • Strong test coverage: 28 new unit tests covering all binding scenarios

The implementation is well-architected with proper separation of concerns, backward compatibility (deprecated apply() method retained), and fail-open error handling to prevent blocking the main request flow.

Confidence Score: 5/5

  • Safe to merge - well-tested feature with proper validation and backward compatibility
  • The implementation demonstrates excellent engineering practices: comprehensive test coverage (28 tests), thorough input validation, performance optimizations with caching, proper error handling with fail-open design, backward compatibility, and clean separation of global vs provider-specific filtering logic. The two-phase application pattern is correctly integrated into the guard pipeline.
  • No files require special attention

Important Files Changed

Filename Overview
src/lib/request-filter-engine.ts Core filtering logic split into applyGlobal() and applyForProvider() with performance optimizations
src/actions/request-filters.ts Added validation logic for binding constraints and helper actions for providers/groups
src/app/v1/_lib/proxy/guard-pipeline.ts Added providerRequestFilter step after provider selection in pipeline
src/app/v1/_lib/proxy/provider-request-filter.ts New guard that applies provider-specific filters after provider selection
tests/unit/request-filter-binding.test.ts Comprehensive test suite with 28 tests covering global, provider, and group binding scenarios

Sequence Diagram

sequenceDiagram
    participant Client
    participant Pipeline as Guard Pipeline
    participant RF as Request Filter
    participant PRF as Provider Request Filter
    participant Engine as Filter Engine
    participant PS as Provider Selector
    participant Provider

    Client->>Pipeline: POST /v1/messages
    Pipeline->>RF: requestFilter step
    RF->>Engine: applyGlobal(session)
    Note over Engine: Apply filters with<br/>bindingType = "global"
    Engine->>Engine: Filter headers/body
    Engine-->>RF: Session modified
    RF-->>Pipeline: Continue
    
    Pipeline->>PS: provider step
    PS->>PS: Select provider based on<br/>load balancing/routing
    PS-->>Pipeline: session.provider set
    
    Pipeline->>PRF: providerRequestFilter step
    PRF->>Engine: applyForProvider(session)
    Note over Engine: Apply filters with<br/>bindingType = "providers"<br/>or "groups"
    Engine->>Engine: Check provider ID match<br/>or group tag match
    Engine->>Engine: Filter headers/body
    Engine-->>PRF: Session modified
    PRF-->>Pipeline: Continue
    
    Pipeline->>Provider: Forward modified request
    Provider-->>Pipeline: Response
    Pipeline-->>Client: Response
Loading

John Doe and others added 4 commits December 29, 2025 20:53
Allow Request Filters to be applied globally, to specific providers, or to provider groups.

## Key Changes

### Database Schema
- Add `bindingType` field ('global' | 'providers' | 'groups')
- Add `providerIds` field (array of provider IDs)
- Add `groupTags` field (array of group tags)
- Migration: 0041_sticky_jackal.sql

### Backend Logic
- Split filter engine into global and provider-specific filters
- `applyGlobal()`: runs BEFORE provider selection
- `applyForProvider()`: runs AFTER provider selection
- Provider matching: checks provider IDs or group tags

### Guard Pipeline
- Add new `providerRequestFilter` guard step
- CHAT_PIPELINE order: [..., provider, providerRequestFilter, messageContext]
- COUNT_TOKENS_PIPELINE includes provider filters

### UI Components
- New: `ProviderMultiSelect` - multi-select for providers
- New: `GroupMultiSelect` - multi-select for group tags
- Updated: `FilterDialog` - binding type selector moved after Name field
- Updated: `FilterTable` - compact layout with icon-only display
- Binding display: Global (icon only), Providers (icon + count), Groups (icon + count)

### Localization
- Add translations for 5 languages (en, ru, ja, zh-CN, zh-TW)
- Column name changed from "Apply To" to "Apply"

### Bug Fixes
- Fix binding type change validation (use explicit undefined check instead of ??)
- Prevent stale binding data when switching types

### Other Improvements
- Compact last 4 columns in filter table (Priority, Apply, Status, Actions)
- Improved UX: binding configuration at top of dialog

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add truncate to name, target, and replacement columns to keep table rows single-line
- Add tooltips on Apply column showing provider names or group tags on hover
- Fetch provider list to display names instead of IDs in tooltips
- Optimize provider lookup with useMemo Map (O(1) access)
- Add i18n translations for tooltip labels (en, ja, ru, zh-CN, zh-TW)
- Optimize request filter engine with caching and early exits

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Set max-w-2xl width for consistency with error rules dialog
- Add max-h-80vh height limit to prevent overflow
- Restructure layout with fixed header and footer
- Make only content area scrollable when form has many fields
- Ensures Cancel/Save buttons and close button always accessible

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add 28 unit tests for RequestFilterEngine binding functionality
- Cover global, provider-specific, and group-specific filters
- Test combined filters and edge cases
- Fix setFiltersForTest to sort by priority (matching reload behavior)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @miraserver, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly upgrades the request filtering system by introducing the ability to apply filters conditionally based on the specific provider or provider group handling a request. This enhancement provides administrators with much greater flexibility and control over how requests and responses are modified. The changes span across the database schema, the core filtering logic, and the user interface, ensuring a robust and efficient implementation for managing these new filter types.

Highlights

  • Granular Request Filter Binding: Request filters can now be bound to specific providers or provider groups, allowing for more precise control over request/response manipulation beyond global application.
  • Two-Phase Filter Application: The request filtering process is now split into two phases: applyGlobal() runs before provider selection, and applyForProvider() runs after, enabling context-aware filtering.
  • Database Schema Updates: The request_filters table has been extended with new columns (binding_type, provider_ids, group_tags) to support the new binding capabilities.
  • Enhanced User Interface: New multi-select components for providers and groups have been added to the filter dialog, alongside improvements to table layout, truncation, and tooltips for better usability.
  • Performance Optimizations: The request filter engine includes several performance enhancements, such as pre-compiled regex caching, set-based O(1) lookups for provider/group matching, and early exit conditions.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added enhancement New feature or request area:UI area:provider area:core size/XL Extra Large PR (> 1000 lines) labels Dec 29, 2025
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

This PR adds provider/group binding functionality for request filters, enabling more granular control over request/response manipulation. The implementation includes database schema changes, engine optimizations, UI improvements, and comprehensive test coverage.

PR Size: XL

  • Lines changed: 3,669 additions, 69 deletions
  • Files changed: 21

Split Suggestion: Given the XL size, consider splitting future changes:

  1. Database schema + repository layer (separate PR)
  2. Engine + proxy integration (separate PR)
  3. UI components (separate PR)

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 1 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 0 0 0
Simplification 0 0 1 0

High Priority Issues (Should Fix)

None identified

Medium Priority Issues (Consider Fixing)

[STANDARD-VIOLATION] Filter table column overflow missing ellipsis class

  • File: src/app/[locale]/settings/request-filters/_components/filter-table.tsx:122
  • Issue: The max-w-xs truncate classes are applied, but truncate alone requires explicit width constraint. While max-w-xs provides max-width, the combination may not produce intended visual truncation in all scenarios.
  • Why this is a problem: Text may overflow visually without proper truncation, affecting the table layout and readability.
  • Suggested fix:
<td className="px-4 py-3 text-sm text-muted-foreground max-w-xs truncate overflow-hidden text-ellipsis">

Or consider using a CSS approach with text-overflow: ellipsis explicitly.

[SIMPLIFY-COMPLEXITY] JSON parsing with silent fallback

  • File: src/app/[locale]/settings/request-filters/_components/filter-dialog.tsx:122-127
  • Issue: The try/catch for JSON parsing silently falls back to string treatment, which could mask actual JSON syntax errors that users should know about.
  • Why this is a problem: Users might have malformed JSON that gets treated as a literal string, leading to unexpected behavior.
  • Suggested fix:
if (raw.length > 0) {
  try {
    parsedReplacement = JSON.parse(raw);
  } catch (e) {
    // Show user the parse error instead of silent fallback
    toast.error("Invalid JSON in replacement field");
    setIsSubmitting(false);
    return;
  }
}

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Good (fail-open pattern with logging)
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Good (existing tests + new tests mentioned)
  • Code clarity - Good

Positive Observations

  1. Excellent Error Handling: The ProxyRequestFilter class implements proper fail-open behavior with logging at src/app/v1/_lib/proxy/request-filter.ts:17-20. Filter failures don't block the main flow.

  2. ReDoS Protection: Both the engine (src/lib/request-filter-engine.ts:62) and actions (src/actions/request-filters.ts:38-41,131) properly validate regex patterns using safe-regex.

  3. Proper Type Safety: The schema uses Drizzle ORM's type-safe .$type<>() method for enums (e.g., requestFilters table schema).

  4. Test Coverage: The existing request-filter-engine.test.ts provides good coverage for the filter engine functionality.

  5. Guard Pipeline Integration: The filter is properly integrated into the pipeline before sensitive word detection (src/app/v1/_lib/proxy/guard-pipeline.ts:164), enabling proper request sanitization.

  6. Server Actions Validation: The actions layer properly validates admin permissions, payload requirements, and ReDoS risks before creating/updating filters.


Automated review by Claude AI

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is an excellent pull request that introduces a significant and well-implemented feature for binding request filters to providers and groups. The two-phase application of filters is a smart design, and the implementation in the proxy pipeline is correct. I'm particularly impressed with the performance optimizations in the RequestFilterEngine, such as pre-compiling regexes and using Sets for faster lookups. The new UI components for selecting providers and groups are user-friendly and feature-complete, and the addition of comprehensive unit tests is a great contribution to the project's stability. I have one minor suggestion for a potential performance optimization. Overall, this is a high-quality contribution.

Comment on lines 289 to 291
? Array.from(filter.groupTagsSet).some((tag) => providerTagsSet!.has(tag))
: false;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This is a good optimization to check for tag intersection using Sets. For potentially better performance, you could iterate over the smaller of the two sets. Since a provider is likely to have fewer tags than a filter might be bound to, iterating over providerTagsSet would be more efficient if filter.groupTagsSet is large.

        matches = filter.groupTagsSet
          ? Array.from(providerTagsSet!).some((tag) => filter.groupTagsSet!.has(tag))
          : false;

Iterate over provider tags (typically 1-3) instead of filter group tags
for faster set intersection check.

Suggested by: @gemini-code-assist

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ding113 ding113 merged commit 8e2115e into ding113:dev Dec 30, 2025
6 of 7 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Dec 30, 2025
@github-actions github-actions bot mentioned this pull request Dec 30, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core area:provider area:UI enhancement New feature or request size/XL Extra Large PR (> 1000 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants

Comments