feat: add provider/group binding for request filters#484
feat: add provider/group binding for request filters#484ding113 merged 5 commits intoding113:devfrom
Conversation
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>
Summary of ChangesHello @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
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
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:
- Database schema + repository layer (separate PR)
- Engine + proxy integration (separate PR)
- 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 truncateclasses are applied, buttruncatealone requires explicit width constraint. Whilemax-w-xsprovides 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
-
Excellent Error Handling: The
ProxyRequestFilterclass implements proper fail-open behavior with logging atsrc/app/v1/_lib/proxy/request-filter.ts:17-20. Filter failures don't block the main flow. -
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 usingsafe-regex. -
Proper Type Safety: The schema uses Drizzle ORM's type-safe
.$type<>()method for enums (e.g.,requestFilterstable schema). -
Test Coverage: The existing
request-filter-engine.test.tsprovides good coverage for the filter engine functionality. -
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. -
Server Actions Validation: The actions layer properly validates admin permissions, payload requirements, and ReDoS risks before creating/updating filters.
Automated review by Claude AI
There was a problem hiding this comment.
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.
src/lib/request-filter-engine.ts
Outdated
| ? Array.from(filter.groupTagsSet).some((tag) => providerTagsSet!.has(tag)) | ||
| : false; | ||
| } |
There was a problem hiding this comment.
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>
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
globalbinding apply to all requestsapplyGlobal()runs before provider selection,applyForProvider()runs afterDatabase Changes
binding_typecolumn (global,providers,groups) torequest_filterstableprovider_ids(integer array) for provider bindinggroup_tags(text array) for group bindingdrizzle/0041_sticky_jackal.sqlUI Improvements
Performance Optimizations
text_replacefiltersFiles Changed
src/drizzle/schema.ts,drizzle/0041_sticky_jackal.sqlsrc/lib/request-filter-engine.tssrc/repository/request-filters.tssrc/actions/request-filters.tssrc/app/v1/_lib/proxy/guard-pipeline.ts,provider-request-filter.ts,request-filter.tsfilter-dialog.tsx,filter-table.tsx,provider-multi-select.tsx,group-multi-select.tsxmessages/{en,ja,ru,zh-CN,zh-TW}/settings.jsontests/unit/request-filter-binding.test.ts(28 new tests)Test plan
🤖 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:
applyGlobal()), provider/group filters run after (applyForProvider())binding_type,provider_ids,group_tagscolumns with indexed lookupsproviderRequestFilterguard executes after provider selection in both CHAT and COUNT_TOKENS pipelinesThe 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
Important Files Changed
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