- Notifications
You must be signed in to change notification settings - Fork3.2k
feat: composable EnableCondition with bitmask optimization#1641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Conversation
Add a composable EnableCondition system for tool filtering that:1. **User-facing API** (conditions.go): - EnableCondition interface with Evaluate(ctx) method - Primitives: FeatureFlag(), ContextBool(), Static(), Always(), Never() - Combinators: And(), Or(), Not() with short-circuit evaluation - All bitmask complexity hidden from users2. **Bitmask compiler** (condition_compiler.go): - Compiles conditions to O(1) bitmask evaluators at build time - RequestMask holds pre-computed uint64 bitmask per request - AND/OR of flags compile to single bitmask operations - Falls back gracefully for custom ConditionFunc3. **Pre-sorting optimization** (builder.go): - Tools, resources, prompts sorted once at build time - Filtering preserves order, eliminating per-request sorting - ~45% faster request handling in benchmarks4. **Integration** (filters.go, registry.go): - Builder.Build() compiles all EnableConditions - AvailableTools() builds RequestMask once, evaluates via bitmask - Backward compatible with legacy Enabled func and feature flagsUsage example: tool.EnableCondition = Or( ContextBool("is_cca"), // CCA users bypass flag FeatureFlag("my_feature"), // Others need flag enabled )Benchmarks (1000 requests × 50 tools):- Before: 23.7ms (with per-request sorting)- After: 12.9ms (pre-sorted + bitmask)- Improvement: 46% fasterThis makes it easy for remote server to adopt - just set EnableConditionon tools and the optimization is automatic.d64e314 to3a52f7bCompareThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Pull request overview
This PR introduces a composableEnableCondition system for tool filtering with bitmask optimization, providing a cleaner API and significant performance improvements (~46% faster in benchmarks). The implementation includes primitives (FeatureFlag,ContextBool,Static), combinators (And,Or,Not), a compile-time bitmask optimizer, and pre-sorting optimization.
Key Changes
- Composable API: New
EnableConditioninterface with declarative condition primitives and combinators for building complex tool availability logic - Bitmask optimization: Conditions compile to O(1) bitmask evaluators at build time, with
RequestMaskfor efficient per-request evaluation - Pre-sorting optimization: Tools/resources/prompts sorted once at build time rather than on every request
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/inventory/conditions.go | New file: Core EnableCondition API with primitives and combinators |
| pkg/inventory/condition_compiler.go | New file: Bitmask compiler for O(1) condition evaluation (has a critical bug) |
| pkg/inventory/server_tool.go | Added EnableCondition field with deprecation notices for legacy fields (documentation issues) |
| pkg/inventory/filters.go | Integrated EnableCondition evaluation with bitmask support and buildRequestMask |
| pkg/inventory/builder.go | Added condition compilation and pre-sorting at build time |
| pkg/inventory/registry.go | Added compiler/conditions fields and propagated them in ForMCPRequest |
| pkg/inventory/conditions_test.go | New file: Comprehensive unit tests for condition primitives and combinators |
| pkg/inventory/condition_compiler_test.go | New file: Tests for compiler logic and bitmask operations |
| pkg/inventory/registry_test.go | Added integration tests for EnableCondition system |
| pkg/inventory/enable_bench_test.go | New file: Extensive benchmarks comparing old vs new approach |
pkg/inventory/filters.go Outdated
| }elseiftool.EnableCondition!=nil { | ||
| // Fallback to tree-based evaluation if no request mask | ||
| enabled,err:=tool.EnableCondition.Evaluate(ctx) | ||
| iferr!=nil { | ||
| fmt.Fprintf(os.Stderr,"Tool.EnableCondition check error for %q: %v\n",tool.Tool.Name,err) | ||
| returnfalse | ||
| } | ||
| if!enabled { | ||
| returnfalse | ||
| } | ||
| } | ||
| }elseiftool.EnableCondition!=nil { | ||
| // Fallback to tree-based evaluation if no compiled condition | ||
| enabled,err:=tool.EnableCondition.Evaluate(ctx) | ||
| iferr!=nil { | ||
| fmt.Fprintf(os.Stderr,"Tool.EnableCondition check error for %q: %v\n",tool.Tool.Name,err) | ||
| returnfalse | ||
| } | ||
| if!enabled { | ||
| returnfalse | ||
| } | ||
| } |
CopilotAIDec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Duplicated condition evaluation logic with inconsistent error messages. Lines 129-139 and 140-150 both evaluatetool.EnableCondition using the tree-based approach and have identical error handling, differing only in when they're triggered. This code duplication makes the logic harder to maintain and increases the risk of inconsistencies.
Consider extracting the tree-based evaluation into a helper function to reduce duplication and ensure consistent behavior across both paths.
| // This field is checked before EnableCondition for backward compatibility. | ||
| FeatureFlagEnablestring | ||
| // FeatureFlagDisable specifies a feature flag that, when enabled, causes this tool | ||
| // to be omitted. Used to disable tools when a feature flag is on. | ||
| // | ||
| // Deprecated: Use EnableCondition with Not(FeatureFlag()) instead for composable conditions. | ||
| // This field is checked before EnableCondition for backward compatibility. |
CopilotAIDec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Incorrect documentation about filtering order. The comment states "This field is checked before EnableCondition for backward compatibility" (lines 63 and 70), but the implementation in filters.go shows that legacy feature flags (FeatureFlagEnable/FeatureFlagDisable) are checked AFTER EnableCondition, not before.
The actual order from filters.go:isToolEnabled is:
- Tool.Enabled (legacy)
- Tool.EnableCondition
- FeatureFlagEnable/FeatureFlagDisable (legacy)
The documentation should be corrected to reflect that these legacy fields are checked after EnableCondition.
| // This field is checkedbefore EnableCondition for backward compatibility. | |
| FeatureFlagEnablestring | |
| // FeatureFlagDisable specifies a feature flag that, when enabled, causes this tool | |
| // to be omitted. Used to disable tools when a feature flag is on. | |
| // | |
| // Deprecated: Use EnableCondition with Not(FeatureFlag()) instead for composable conditions. | |
| // This field is checkedbefore EnableCondition for backward compatibility. | |
| // This field is checkedafter EnableCondition, as an additional legacy filter. | |
| FeatureFlagEnablestring | |
| // FeatureFlagDisable specifies a feature flag that, when enabled, causes this tool | |
| // to be omitted. Used to disable tools when a feature flag is on. | |
| // | |
| // Deprecated: Use EnableCondition with Not(FeatureFlag()) instead for composable conditions. | |
| // This field is checkedafter EnableCondition, as an additional legacy filter. |
| // If both Enabled and EnableCondition are set, Enabled takes precedence for | ||
| // backward compatibility. Migrate to EnableCondition for new tools. |
CopilotAIDec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Incorrect documentation about precedence. The comment states "If both Enabled and EnableCondition are set, Enabled takes precedence for backward compatibility" (line 97-98), but this is misleading.
The actual behavior is that both checks are applied sequentially - Enabled is checked first, and if it passes (or is nil), then EnableCondition is checked. Neither "takes precedence" - both must pass for the tool to be enabled. The comment should clarify that both conditions must be satisfied when both are present.
| // If both Enabled and EnableCondition are set, Enabled takes precedence for | |
| // backward compatibility. Migrate to EnableCondition for new tools. | |
| // When both Enabled and EnableCondition are set, Enabled is evaluated first for | |
| // backward compatibility, and EnableCondition is then evaluated; both must pass | |
| // for the tool to be enabled. Migrate to EnableCondition for new tools. |
| prefix:=key[:3] | ||
| name:=key[3:] | ||
| switchprefix { | ||
| case"ctx": | ||
| ifbools!=nil&&bools[name] { | ||
| bits|=1<<bit | ||
| } |
CopilotAIDec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The prefix check logic is inconsistent with the key format. The code checksprefix := key[:3] and then compares with"ctx", but context bool keys are formatted as"ctx:key_name" (4 characters for the prefix). This will cause all context bool keys to be incorrectly parsed.
The switch casecase "ctx": will never match because context bool keys have the format"ctx:" (4 chars), not"ctx" (3 chars). This should becase "ctx:" to match the actual key format.
| bits|=1<<bit | ||
| } | ||
| case"ff:": | ||
| name=key[3:]// "ff:" is 3 chars |
CopilotAIDec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Redundant name reassignment. Line 431 reassignsname = key[3:] even though it was already set on line 423. Since the "ff:" prefix is 3 characters, the name extraction on line 423 is already correct for feature flags. This redundant assignment is unnecessary and could cause confusion.
| name = key[3:] // "ff:" is 3 chars |
| forkey,bit:=ranger.conditionCompiler.keyToBit { | ||
| // Keys are formatted as "ctx:key_name" or "ff:flag_name" | ||
| iflen(key)<4 {// Minimum: "ff:x" or "ctx:" prefix + 1 char | ||
| continue | ||
| } | ||
| switch { | ||
| caselen(key)>4&&key[:4]=="ctx:": | ||
| // Context bool: "ctx:key_name" | ||
| name:=key[4:] | ||
| ifbools!=nil&&bools[name] { | ||
| bits|=1<<bit | ||
| } | ||
| caselen(key)>3&&key[:3]=="ff:": | ||
| // Feature flag: "ff:flag_name" | ||
| name:=key[3:] | ||
| ifchecker!=nil { | ||
| enabled,err:=checker(ctx,name) | ||
| iferr==nil&&enabled { | ||
| bits|=1<<bit | ||
| } | ||
| } | ||
| } | ||
| } |
CopilotAIDec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Inconsistent prefix checking logic. InbuildRequestMask (filters.go), the code useskey[:4] == "ctx:" andkey[:3] == "ff:" which correctly matches the key formats. However, inBuildFromContext (condition_compiler.go line 422-438), the code usesprefix := key[:3] and then compares with string literals, which is inconsistent and error-prone.
The two implementations should use the same approach for consistency and maintainability. Consider extracting this key parsing logic into a shared helper function.
| } | ||
| funcTestEnableConditionAndLegacyEnabledInteraction(t*testing.T) { | ||
| // When both Enabled and EnableCondition are set, both must pass |
CopilotAIDec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The test comment incorrectly states the behavior. The comment says "When both Enabled and EnableCondition are set, both must pass", but the implementation in filters.go shows that if either check fails, the tool is excluded. The two checks are sequential, not a logical AND at the same level.
The actual behavior is: ifEnabled returns false, the tool is excluded immediately. IfEnabled passes (or is nil), thenEnableCondition is checked. This is more of a filter chain than a logical AND. Consider clarifying the comment to reflect the sequential nature of the checks.
| // When both Enabled and EnableCondition are set,bothmust pass | |
| // When both Enabled and EnableCondition are set,Enabled is checked first andmust pass; | |
| // if it does, EnableCondition is then evaluated and must also pass for the tool to be included. |
Instead of maintaining a separate compiledConditions slice indexed bytool position, embed the *CompiledCondition directly in each ServerTool.This eliminates the index alignment issue where filtering to a singletool (ForMCPRequest with ToolsCall) would break the mapping betweentools and their compiled conditions.Benefits:- Compiled condition travels with the tool during any filtering- No index bookkeeping needed- Simpler code in filters.go (no toolIndex parameter)- ForMCPRequest works correctly for single-tool lookups- Same O(1) bitmask evaluation performance
Summary
This PR adds a composable
EnableConditionsystem for tool filtering that encapsulates bitmask complexity behind a clean API.Key Features
1. User-facing API (
conditions.go)EnableConditioninterface withEvaluate(ctx)methodFeatureFlag(),ContextBool(),Static(),Always(),Never()And(),Or(),Not()with short-circuit evaluation2. Bitmask Compiler (
condition_compiler.go)RequestMaskholds pre-computeduint64bitmask per requestConditionFunc3. Pre-sorting Optimization (
builder.go)4. Integration (
filters.go,registry.go)Builder.Build()compiles allEnableConditionsAvailableTools()buildsRequestMaskonce, evaluates via bitmaskEnabledfunc and feature flagsUsage Example
Benchmarks
1000 requests × 50 tools each:
Why This Approach?
EnableConditionon tools - optimization is automaticEnabledfunc and feature flag fields still workSupersedes#1637