1+ using Microsoft . Extensions . Logging ;
2+ using Microsoft . Extensions . Options ;
3+ using NLWebNet . Models ;
4+
5+ namespace NLWebNet . Services ;
6+
7+ /// <summary>
8+ /// Implementation of tool selection logic that routes queries to appropriate tools based on intent.
9+ /// </summary>
10+ public class ToolSelector : IToolSelector
11+ {
12+ private readonly ILogger < ToolSelector > _logger ;
13+ private readonly NLWebOptions _options ;
14+
15+ public ToolSelector ( ILogger < ToolSelector > logger , IOptions < NLWebOptions > options )
16+ {
17+ _logger = logger ?? throw new ArgumentNullException ( nameof ( logger ) ) ;
18+ _options = options ? . Value ?? throw new ArgumentNullException ( nameof ( options ) ) ;
19+ }
20+
21+ /// <inheritdoc />
22+ public async Task < string ? > SelectToolAsync ( NLWebRequest request , CancellationToken cancellationToken = default )
23+ {
24+ if ( ! ShouldSelectTool ( request ) )
25+ {
26+ _logger . LogDebug ( "Tool selection not needed for request {QueryId}" , request . QueryId ) ;
27+ return null ;
28+ }
29+
30+ _logger . LogDebug ( "Selecting tool for query: {Query}" , request . Query ) ;
31+
32+ // Simple intent-based tool selection
33+ // In a full implementation, this would use more sophisticated intent analysis
34+ var selectedTool = await AnalyzeQueryIntentAsync ( request . Query , cancellationToken ) ;
35+
36+ _logger . LogDebug ( "Selected tool: {Tool} for query {QueryId}" , selectedTool ?? "none" , request . QueryId ) ;
37+ return selectedTool ;
38+ }
39+
40+ /// <inheritdoc />
41+ public bool ShouldSelectTool ( NLWebRequest request )
42+ {
43+ // Don't perform tool selection if:
44+ // 1. Tool selection is disabled in configuration
45+ // 2. Generate mode is used (maintain existing behavior)
46+ // 3. Request already has a decontextualized query (already processed)
47+
48+ if ( ! _options . ToolSelectionEnabled )
49+ {
50+ return false ;
51+ }
52+
53+ if ( request . Mode == QueryMode . Generate )
54+ {
55+ _logger . LogDebug ( "Skipping tool selection for Generate mode to maintain existing behavior" ) ;
56+ return false ;
57+ }
58+
59+ if ( ! string . IsNullOrEmpty ( request . DecontextualizedQuery ) )
60+ {
61+ _logger . LogDebug ( "Skipping tool selection for request with decontextualized query" ) ;
62+ return false ;
63+ }
64+
65+ return true ;
66+ }
67+
68+ /// <summary>
69+ /// Analyzes the query intent to determine the appropriate tool.
70+ /// This is a simplified implementation - production would use more sophisticated NLP.
71+ /// </summary>
72+ private async Task < string ? > AnalyzeQueryIntentAsync ( string query , CancellationToken cancellationToken )
73+ {
74+ await Task . Delay ( 1 , cancellationToken ) ; // Simulate async analysis
75+
76+ var queryLower = query . ToLowerInvariant ( ) ;
77+
78+ // Basic keyword-based intent detection
79+ // In production, this would use ML models or more sophisticated analysis
80+
81+ if ( ContainsKeywords ( queryLower , "search" , "find" , "look for" , "locate" ) )
82+ {
83+ return "search" ;
84+ }
85+
86+ if ( ContainsKeywords ( queryLower , "compare" , "difference" , "versus" , "vs" , "contrast" ) )
87+ {
88+ return "compare" ;
89+ }
90+
91+ if ( ContainsKeywords ( queryLower , "details" , "information about" , "tell me about" , "describe" ) )
92+ {
93+ return "details" ;
94+ }
95+
96+ if ( ContainsKeywords ( queryLower , "recommend" , "suggest" , "what should" , "ensemble" , "set of" ) )
97+ {
98+ return "ensemble" ;
99+ }
100+
101+ // Default to search tool for general queries
102+ return "search" ;
103+ }
104+
105+ private static bool ContainsKeywords ( string text , params string [ ] keywords )
106+ {
107+ return keywords . Any ( keyword => text . Contains ( keyword ) ) ;
108+ }
109+ }
0 commit comments