Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Open
SamMorrowDrums wants to merge2 commits intomain
base:main
Choose a base branch
Loading
fromperf/composable-enable-conditions

Conversation

@SamMorrowDrums
Copy link
Collaborator

Summary

This PR adds a composableEnableCondition system for tool filtering that encapsulates bitmask complexity behind a clean API.

Key Features

1. User-facing API (conditions.go)

  • EnableCondition interface withEvaluate(ctx) method
  • Primitives:FeatureFlag(),ContextBool(),Static(),Always(),Never()
  • Combinators:And(),Or(),Not() with short-circuit evaluation
  • All bitmask complexity hidden from users

2. Bitmask Compiler (condition_compiler.go)

  • Compiles conditions to O(1) bitmask evaluators at build time
  • RequestMask holds pre-computeduint64 bitmask per request
  • AND/OR of flags compile to single bitmask operations
  • Falls back gracefully for customConditionFunc

3. 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 benchmarks

4. Integration (filters.go,registry.go)

  • Builder.Build() compiles allEnableConditions
  • AvailableTools() buildsRequestMask once, evaluates via bitmask
  • Backward compatible with legacyEnabled func and feature flags

Usage Example

// Simple feature flagtool.EnableCondition=FeatureFlag("my_feature")// CCA bypass pattern (common in remote server)tool.EnableCondition=Or(ContextBool("is_cca"),// CCA users bypass flagFeatureFlag("my_feature"),// Others need flag enabled)// Complex conditiontool.EnableCondition=And(ContextBool("is_premium"),Or(FeatureFlag("beta_access"),ContextBool("is_staff"),    ),Not(FeatureFlag("kill_switch")),)

Benchmarks

1000 requests × 50 tools each:

MetricBeforeAfterImprovement
Time23.7ms12.9ms46% faster
Allocations150001200020% fewer

Why This Approach?

  1. Easy to adopt: Remote server just setsEnableCondition on tools - optimization is automatic
  2. Composable: Build complex conditions from simple primitives
  3. Hidden complexity: Users don't need to know about bitmasks
  4. Backward compatible: ExistingEnabled func and feature flag fields still work

Supersedes#1637

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.
CopilotAI review requested due to automatic review settingsDecember 18, 2025 13:21
@SamMorrowDrumsSamMorrowDrums requested a review froma team as acode ownerDecember 18, 2025 13:21
@SamMorrowDrumsSamMorrowDrumsforce-pushed theperf/composable-enable-conditions branch fromd64e314 to3a52f7bCompareDecember 18, 2025 13:25
Copy link
Contributor

CopilotAI left a 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: NewEnableCondition interface with declarative condition primitives and combinators for building complex tool availability logic
  • Bitmask optimization: Conditions compile to O(1) bitmask evaluators at build time, withRequestMask for 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
FileDescription
pkg/inventory/conditions.goNew file: Core EnableCondition API with primitives and combinators
pkg/inventory/condition_compiler.goNew file: Bitmask compiler for O(1) condition evaluation (has a critical bug)
pkg/inventory/server_tool.goAdded EnableCondition field with deprecation notices for legacy fields (documentation issues)
pkg/inventory/filters.goIntegrated EnableCondition evaluation with bitmask support and buildRequestMask
pkg/inventory/builder.goAdded condition compilation and pre-sorting at build time
pkg/inventory/registry.goAdded compiler/conditions fields and propagated them in ForMCPRequest
pkg/inventory/conditions_test.goNew file: Comprehensive unit tests for condition primitives and combinators
pkg/inventory/condition_compiler_test.goNew file: Tests for compiler logic and bitmask operations
pkg/inventory/registry_test.goAdded integration tests for EnableCondition system
pkg/inventory/enable_bench_test.goNew file: Extensive benchmarks comparing old vs new approach

Comment on lines 129 to 150
}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
}
}

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.

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +70
// 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.

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:

  1. Tool.Enabled (legacy)
  2. Tool.EnableCondition
  3. FeatureFlagEnable/FeatureFlagDisable (legacy)

The documentation should be corrected to reflect that these legacy fields are checked after EnableCondition.

Suggested change
// 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.

Copilot uses AI. Check for mistakes.
Comment on lines +97 to +98
// If both Enabled and EnableCondition are set, Enabled takes precedence for
// backward compatibility. Migrate to EnableCondition for new tools.

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.

Suggested change
// 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.

Copilot uses AI. Check for mistakes.
Comment on lines +422 to +429
prefix:=key[:3]
name:=key[3:]

switchprefix {
case"ctx":
ifbools!=nil&&bools[name] {
bits|=1<<bit
}

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.

Copilot uses AI. Check for mistakes.
bits|=1<<bit
}
case"ff:":
name=key[3:]// "ff:" is 3 chars

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.

Suggested change
name = key[3:] // "ff:" is 3 chars

Copilot uses AI. Check for mistakes.
Comment on lines +67 to +90
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
}
}
}
}

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.

Copilot uses AI. Check for mistakes.
}

funcTestEnableConditionAndLegacyEnabledInteraction(t*testing.T) {
// When both Enabled and EnableCondition are set, both must pass

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.

Suggested change
// 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.

Copilot uses AI. Check for mistakes.
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
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

Copilot code reviewCopilotCopilot left review comments

At least 1 approving review is required to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

@SamMorrowDrums

[8]ページ先頭

©2009-2025 Movatter.jp