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+ /// <summary>
16+ /// Constants for tool names and associated keywords
17+ /// </summary>
18+ private static class ToolConstants
19+ {
20+ // Tool names
21+ public const string SearchTool = "search" ;
22+ public const string CompareTool = "compare" ;
23+ public const string DetailsTool = "details" ;
24+ public const string EnsembleTool = "ensemble" ;
25+
26+ // Keywords for each tool
27+ public static readonly string [ ] SearchKeywords = { "search" , "find" , "look for" , "locate" } ;
28+ public static readonly string [ ] CompareKeywords = { "compare" , "difference" , "versus" , "vs" , "contrast" } ;
29+ public static readonly string [ ] DetailsKeywords = { "details" , "information about" , "tell me about" , "describe" } ;
30+ public static readonly string [ ] EnsembleKeywords = { "recommend" , "suggest" , "what should" , "ensemble" , "set of" } ;
31+ }
32+
33+ public ToolSelector ( ILogger < ToolSelector > logger , IOptions < NLWebOptions > options )
34+ {
35+ _logger = logger ?? throw new ArgumentNullException ( nameof ( logger ) ) ;
36+ _options = options ? . Value ?? throw new ArgumentNullException ( nameof ( options ) ) ;
37+ }
38+
39+ /// <inheritdoc />
40+ public async Task < string ? > SelectToolAsync ( NLWebRequest request , CancellationToken cancellationToken = default )
41+ {
42+ if ( ! ShouldSelectTool ( request ) )
43+ {
44+ _logger . LogDebug ( "Tool selection not needed for request {QueryId}" , request . QueryId ) ;
45+ return null ;
46+ }
47+
48+ _logger . LogDebug ( "Selecting tool for query: {Query}" , request . Query ) ;
49+
50+ // Simple intent-based tool selection
51+ // In a full implementation, this would use more sophisticated intent analysis
52+ var selectedTool = await AnalyzeQueryIntentAsync ( request . Query , cancellationToken ) ;
53+
54+ _logger . LogDebug ( "Selected tool: {Tool} for query {QueryId}" , selectedTool ?? "none" , request . QueryId ) ;
55+ return selectedTool ;
56+ }
57+
58+ /// <inheritdoc />
59+ public bool ShouldSelectTool ( NLWebRequest request )
60+ {
61+ // Don't perform tool selection if:
62+ // 1. Tool selection is disabled in configuration
63+ // 2. Generate mode is used (maintain existing behavior)
64+ // 3. Request already has a decontextualized query (already processed)
65+
66+ if ( ! _options . ToolSelectionEnabled )
67+ {
68+ return false ;
69+ }
70+
71+ if ( request . Mode == QueryMode . Generate )
72+ {
73+ _logger . LogDebug ( "Skipping tool selection for Generate mode to maintain existing behavior" ) ;
74+ return false ;
75+ }
76+
77+ if ( ! string . IsNullOrEmpty ( request . DecontextualizedQuery ) )
78+ {
79+ _logger . LogDebug ( "Skipping tool selection for request with decontextualized query" ) ;
80+ return false ;
81+ }
82+
83+ return true ;
84+ }
85+
86+ /// <summary>
87+ /// Analyzes the query intent to determine the appropriate tool.
88+ /// This is a simplified implementation - production would use more sophisticated NLP.
89+ /// </summary>
90+ private Task < string ? > AnalyzeQueryIntentAsync ( string query , CancellationToken cancellationToken )
91+ {
92+ var queryLower = query . ToLowerInvariant ( ) ;
93+
94+ // Basic keyword-based intent detection
95+ // In production, this would use ML models or more sophisticated analysis
96+
97+ if ( ContainsKeywords ( queryLower , ToolConstants . SearchKeywords ) )
98+ {
99+ return Task . FromResult < string ? > ( ToolConstants . SearchTool ) ;
100+ }
101+
102+ if ( ContainsKeywords ( queryLower , ToolConstants . CompareKeywords ) )
103+ {
104+ return Task . FromResult < string ? > ( ToolConstants . CompareTool ) ;
105+ }
106+
107+ if ( ContainsKeywords ( queryLower , ToolConstants . DetailsKeywords ) )
108+ {
109+ return Task . FromResult < string ? > ( ToolConstants . DetailsTool ) ;
110+ }
111+
112+ if ( ContainsKeywords ( queryLower , ToolConstants . EnsembleKeywords ) )
113+ {
114+ return Task . FromResult < string ? > ( ToolConstants . EnsembleTool ) ;
115+ }
116+
117+ // Default to search tool for general queries
118+ return Task . FromResult < string ? > ( ToolConstants . SearchTool ) ;
119+ }
120+
121+ private static bool ContainsKeywords ( string text , string [ ] keywords )
122+ {
123+ return keywords . Any ( keyword => text . Contains ( keyword ) ) ;
124+ }
125+ }
0 commit comments