Module: Legion::MCP::UsageFilter
- Defined in:
- lib/legion/mcp/usage_filter.rb
Constant Summary collapse
- ESSENTIAL_TOOLS =
%w[ legion.do legion.tools legion.run_task legion.get_status legion.describe_runner ].freeze
- FREQUENCY_WEIGHT =
0.5- RECENCY_WEIGHT =
0.3- KEYWORD_WEIGHT =
0.2- BASELINE_SCORE =
0.1
Class Method Summary collapse
- .keyword_match(tool_name, keywords) ⇒ Object
- .prune_dead_tools(tool_names, prune_after_seconds: 86_400 * 30) ⇒ Object
- .ranked_tools(tool_names, limit: nil, keywords: []) ⇒ Object
- .recency_decay(last_used) ⇒ Object
- .score_tools(tool_names, keywords: []) ⇒ Object
Class Method Details
.keyword_match(tool_name, keywords) ⇒ Object
78 79 80 81 82 83 |
# File 'lib/legion/mcp/usage_filter.rb', line 78 def keyword_match(tool_name, keywords) return 0.0 if keywords.nil? || keywords.empty? hits = keywords.count { |kw| tool_name.include?(kw.to_s) } hits.to_f / keywords.size end |
.prune_dead_tools(tool_names, prune_after_seconds: 86_400 * 30) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/legion/mcp/usage_filter.rb', line 52 def prune_dead_tools(tool_names, prune_after_seconds: 86_400 * 30) stats = Observer.stats window = stats[:since] elapsed = window ? (Time.now - window) : 0 return tool_names if elapsed < prune_after_seconds all_stats = Observer.all_tool_stats tool_names.reject do |name| next false if ESSENTIAL_TOOLS.include?(name) calls = all_stats.dig(name, :call_count) || 0 calls.zero? end end |
.ranked_tools(tool_names, limit: nil, keywords: []) ⇒ Object
46 47 48 49 50 |
# File 'lib/legion/mcp/usage_filter.rb', line 46 def ranked_tools(tool_names, limit: nil, keywords: []) scores = score_tools(tool_names, keywords: keywords) ranked = tool_names.sort_by { |n| -scores.fetch(n, BASELINE_SCORE) } limit ? ranked.first(limit) : ranked end |
.recency_decay(last_used) ⇒ Object
68 69 70 71 72 73 74 75 76 |
# File 'lib/legion/mcp/usage_filter.rb', line 68 def recency_decay(last_used) return 0.0 unless last_used age_seconds = Time.now - last_used return 1.0 if age_seconds <= 0 decay = 1.0 - (age_seconds / 86_400.0) decay.clamp(0.0, 1.0) end |
.score_tools(tool_names, keywords: []) ⇒ Object
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/legion/mcp/usage_filter.rb', line 17 def score_tools(tool_names, keywords: []) all_stats = Observer.all_tool_stats call_counts = tool_names.map { |n| all_stats.dig(n, :call_count) || 0 } max_calls = call_counts.max || 0 tool_names.each_with_object({}) do |name, hash| stats = all_stats[name] freq_score = if max_calls.positive? && stats (stats[:call_count].to_f / max_calls) * FREQUENCY_WEIGHT else 0.0 end rec_score = if stats&.dig(:last_used) recency_decay(stats[:last_used]) * RECENCY_WEIGHT else 0.0 end kw_score = keyword_match(name, keywords) * KEYWORD_WEIGHT total = freq_score + rec_score + kw_score total = BASELINE_SCORE if total.zero? hash[name] = total.round(6) end end |