Skip to content

Commit ddec0fb

Browse files
Copilotjongalloway
andcommitted
Complete Advanced Tool System implementation with working tool handlers
Co-authored-by: jongalloway <[email protected]>
1 parent 0dd20e3 commit ddec0fb

File tree

7 files changed

+396
-1324
lines changed

7 files changed

+396
-1324
lines changed

src/NLWebNet/Services/CompareToolHandler.cs

Lines changed: 47 additions & 225 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ public class CompareToolHandler : BaseToolHandler
1515
public CompareToolHandler(
1616
ILogger<CompareToolHandler> logger,
1717
IOptions<NLWebOptions> options,
18-
IQueryProcessor queryProcessor, IResultGenerator resultGenerator)
18+
IQueryProcessor queryProcessor,
19+
IResultGenerator resultGenerator)
1920
: base(logger, options, queryProcessor, resultGenerator)
2021
{
2122
}
@@ -41,22 +42,25 @@ public override async Task<NLWebResponse> ExecuteAsync(NLWebRequest request, Can
4142

4243
Logger.LogDebug("Comparing '{Item1}' vs '{Item2}'", comparisonItems.Item1, comparisonItems.Item2);
4344

44-
// Gather information about both items
45-
var item1Results = await GatherItemInformation(comparisonItems.Item1, request, cancellationToken);
46-
var item2Results = await GatherItemInformation(comparisonItems.Item2, request, cancellationToken);
45+
// Create comparison query
46+
var comparisonQuery = $"{comparisonItems.Item1} vs {comparisonItems.Item2} comparison differences";
47+
48+
// Generate comparison results
49+
var searchResults = await ResultGenerator.GenerateListAsync(comparisonQuery, request.Site, cancellationToken);
50+
var resultsList = searchResults.ToList();
4751

4852
// Create structured comparison results
49-
var comparisonResponse = CreateComparisonResponse(
50-
request, comparisonItems.Item1, comparisonItems.Item2,
51-
item1Results, item2Results);
53+
var comparisonResults = CreateComparisonResults(resultsList, comparisonItems.Item1, comparisonItems.Item2);
5254

5355
stopwatch.Stop();
54-
comparisonResponse.ProcessingTimeMs = stopwatch.ElapsedMilliseconds;
55-
comparisonResponse.Message = $"Comparison completed between '{comparisonItems.Item1}' and '{comparisonItems.Item2}'";
56+
57+
var response = CreateSuccessResponse(request, comparisonResults, stopwatch.ElapsedMilliseconds);
58+
response.ProcessedQuery = comparisonQuery;
59+
response.Summary = $"Comparison completed between '{comparisonItems.Item1}' and '{comparisonItems.Item2}'";
5660

5761
Logger.LogDebug("Compare tool completed in {ElapsedMs}ms", stopwatch.ElapsedMilliseconds);
5862

59-
return comparisonResponse;
63+
return response;
6064
}
6165
catch (Exception ex)
6266
{
@@ -119,19 +123,15 @@ public override int GetPriority(NLWebRequest request)
119123
@"(.+?)\s+(?:vs|versus)\s+(.+)",
120124
// "difference between A and B"
121125
@"difference\s+between\s+(.+?)\s+and\s+(.+)",
122-
// "A or B" (when asking which is better)
123-
@"(.+?)\s+or\s+(.+?)(?:\s+which|$)",
124-
// "A and B comparison"
125-
@"(.+?)\s+and\s+(.+?)\s+comparison",
126126
};
127127

128128
foreach (var pattern in patterns)
129129
{
130130
var match = Regex.Match(queryLower, pattern, RegexOptions.IgnoreCase);
131131
if (match.Success && match.Groups.Count > 2)
132132
{
133-
var item1 = CleanComparisonItem(match.Groups[1].Value);
134-
var item2 = CleanComparisonItem(match.Groups[2].Value);
133+
var item1 = match.Groups[1].Value.Trim();
134+
var item2 = match.Groups[2].Value.Trim();
135135

136136
if (!string.IsNullOrWhiteSpace(item1) && !string.IsNullOrWhiteSpace(item2))
137137
{
@@ -144,226 +144,48 @@ public override int GetPriority(NLWebRequest request)
144144
}
145145

146146
/// <summary>
147-
/// Cleans up extracted comparison items by removing noise words.
148-
/// </summary>
149-
private string CleanComparisonItem(string item)
150-
{
151-
if (string.IsNullOrWhiteSpace(item))
152-
return string.Empty;
153-
154-
var cleaned = item.Trim();
155-
156-
// Remove common noise words from the beginning
157-
var prefixNoise = new[] { "the", "a", "an", "which", "what", "how" };
158-
foreach (var noise in prefixNoise)
159-
{
160-
if (cleaned.StartsWith(noise + " ", StringComparison.OrdinalIgnoreCase))
161-
{
162-
cleaned = cleaned.Substring(noise.Length + 1).Trim();
163-
}
164-
}
165-
166-
// Remove common noise words from the end
167-
var suffixNoise = new[] { "better", "worse", "best", "good", "bad" };
168-
foreach (var noise in suffixNoise)
169-
{
170-
if (cleaned.EndsWith(" " + noise, StringComparison.OrdinalIgnoreCase))
171-
{
172-
cleaned = cleaned.Substring(0, cleaned.Length - noise.Length - 1).Trim();
173-
}
174-
}
175-
176-
return cleaned;
177-
}
178-
179-
/// <summary>
180-
/// Gathers information about a specific item for comparison.
181-
/// </summary>
182-
private async Task<IList<NLWebResult>> GatherItemInformation(string item, NLWebRequest originalRequest, CancellationToken cancellationToken)
183-
{
184-
// Create a focused query for this specific item
185-
var itemQuery = $"{item} features overview specifications";
186-
187-
var itemRequest = new NLWebRequest
188-
{
189-
QueryId = originalRequest.QueryId,
190-
Query = itemQuery,
191-
Mode = originalRequest.Mode,
192-
Site = originalRequest.Site,
193-
MaxResults = 5, // Limit results per item
194-
TimeoutSeconds = originalRequest.TimeoutSeconds,
195-
DecontextualizedQuery = originalRequest.DecontextualizedQuery,
196-
Context = originalRequest.Context
197-
};
198-
199-
try
200-
{
201-
var response = await QueryProcessor.ProcessQueryAsync(itemRequest, cancellationToken);
202-
return response.Success ? response.Results : new List<NLWebResult>();
203-
}
204-
catch (Exception ex)
205-
{
206-
Logger.LogWarning(ex, "Failed to gather information for item '{Item}'", item);
207-
return new List<NLWebResult>();
208-
}
209-
}
210-
211-
/// <summary>
212-
/// Creates a structured comparison response from the gathered information.
147+
/// Creates structured comparison results.
213148
/// </summary>
214-
private NLWebResponse CreateComparisonResponse(
215-
NLWebRequest request,
216-
string item1,
217-
string item2,
218-
IList<NLWebResult> item1Results,
219-
IList<NLWebResult> item2Results)
149+
private IList<NLWebResult> CreateComparisonResults(IList<NLWebResult> results, string item1, string item2)
220150
{
221151
var comparisonResults = new List<NLWebResult>();
222152

223153
// Create summary comparison result
224-
var summaryResult = CreateComparisonSummary(item1, item2, item1Results, item2Results);
225-
comparisonResults.Add(summaryResult);
226-
227-
// Add detailed results for item 1
228-
var item1Section = CreateItemSection(item1, item1Results, "A");
229-
comparisonResults.AddRange(item1Section);
230-
231-
// Add detailed results for item 2
232-
var item2Section = CreateItemSection(item2, item2Results, "B");
233-
comparisonResults.AddRange(item2Section);
234-
235-
// Add side-by-side comparison if we have good data
236-
if (item1Results.Any() && item2Results.Any())
237-
{
238-
var sideBySideResult = CreateSideBySideComparison(item1, item2, item1Results, item2Results);
239-
comparisonResults.Add(sideBySideResult);
154+
comparisonResults.Add(CreateToolResult(
155+
$"Comparison: {item1} vs {item2}",
156+
$"Side-by-side comparison analysis of {item1} and {item2}",
157+
"",
158+
"Compare",
159+
1.0
160+
));
161+
162+
// Add relevant comparison results
163+
var relevantResults = results
164+
.Where(r => IsRelevantForComparison(r, item1, item2))
165+
.Take(8)
166+
.ToList();
167+
168+
foreach (var result in relevantResults)
169+
{
170+
comparisonResults.Add(CreateToolResult(
171+
$"[Compare] {result.Name}",
172+
result.Description,
173+
result.Url,
174+
result.Site ?? "Compare",
175+
result.Score
176+
));
240177
}
241178

242-
return CreateSuccessResponse(request, comparisonResults, 0);
179+
return comparisonResults;
243180
}
244181

245182
/// <summary>
246-
/// Creates a high-level comparison summary.
183+
/// Checks if a result is relevant for comparison.
247184
/// </summary>
248-
private NLWebResult CreateComparisonSummary(string item1, string item2, IList<NLWebResult> item1Results, IList<NLWebResult> item2Results)
185+
private bool IsRelevantForComparison(NLWebResult result, string item1, string item2)
249186
{
250-
var summary = $"Comparison between {item1} and {item2}:\n\n";
251-
252-
if (item1Results.Any())
253-
{
254-
summary += $"**{item1}**: {GetBestSummary(item1Results)}\n\n";
255-
}
256-
257-
if (item2Results.Any())
258-
{
259-
summary += $"**{item2}**: {GetBestSummary(item2Results)}\n\n";
260-
}
261-
262-
if (!item1Results.Any() && !item2Results.Any())
263-
{
264-
summary += "Limited information available for detailed comparison.";
265-
}
266-
267-
return new NLWebResult
268-
{
269-
Title = $"Comparison: {item1} vs {item2}",
270-
Summary = summary,
271-
Url = string.Empty,
272-
Site = "Compare",
273-
Content = "Structured comparison analysis",
274-
Timestamp = DateTime.UtcNow
275-
};
276-
}
277-
278-
/// <summary>
279-
/// Creates a section of results for a specific item.
280-
/// </summary>
281-
private IList<NLWebResult> CreateItemSection(string item, IList<NLWebResult> results, string section)
282-
{
283-
var sectionResults = new List<NLWebResult>();
284-
285-
// Add section header
286-
sectionResults.Add(new NLWebResult
287-
{
288-
Title = $"Option {section}: {item}",
289-
Summary = $"Information about {item}",
290-
Url = string.Empty,
291-
Site = "Compare",
292-
Content = string.Empty,
293-
Timestamp = DateTime.UtcNow
294-
});
295-
296-
// Add the best results for this item
297-
var bestResults = results.Take(3).ToList();
298-
foreach (var result in bestResults)
299-
{
300-
if (result is NLWebResult webResult)
301-
{
302-
var enhancedResult = new NLWebResult
303-
{
304-
Title = $"{item}: {webResult.Name}",
305-
Summary = webResult.Description,
306-
Url = webResult.Url,
307-
Site = webResult.Site ?? "Compare",
308-
};
309-
sectionResults.Add(enhancedResult);
310-
}
311-
}
312-
313-
return sectionResults;
314-
}
315-
316-
/// <summary>
317-
/// Creates a side-by-side comparison result.
318-
/// </summary>
319-
private NLWebResult CreateSideBySideComparison(string item1, string item2, IList<NLWebResult> item1Results, IList<NLWebResult> item2Results)
320-
{
321-
var comparison = $"**Side-by-Side Comparison**\n\n";
322-
comparison += $"| Aspect | {item1} | {item2} |\n";
323-
comparison += "|--------|---------|----------|\n";
324-
325-
// Extract key aspects from both sets of results
326-
var item1Summary = GetBestSummary(item1Results);
327-
var item2Summary = GetBestSummary(item2Results);
328-
329-
comparison += $"| Overview | {TruncateForTable(item1Summary)} | {TruncateForTable(item2Summary)} |\n";
330-
331-
return new NLWebResult
332-
{
333-
Title = $"Side-by-Side: {item1} vs {item2}",
334-
Summary = comparison,
335-
Url = string.Empty,
336-
Site = "Compare",
337-
Content = "Detailed side-by-side comparison table",
338-
Timestamp = DateTime.UtcNow
339-
};
340-
}
341-
342-
/// <summary>
343-
/// Gets the best summary from a collection of results.
344-
/// </summary>
345-
private string GetBestSummary(IList<NLWebResult> results)
346-
{
347-
var bestResult = results
348-
.Where(r => !string.IsNullOrWhiteSpace(r.Description))
349-
.OrderByDescending(r => r.Description?.Length ?? 0)
350-
.FirstOrDefault();
351-
352-
return bestResult?.Description ?? "No detailed information available";
353-
}
354-
355-
/// <summary>
356-
/// Truncates text for table display.
357-
/// </summary>
358-
private string TruncateForTable(string text)
359-
{
360-
if (string.IsNullOrWhiteSpace(text))
361-
return "N/A";
362-
363-
const int maxLength = 100;
364-
if (text.Length <= maxLength)
365-
return text;
366-
367-
return text.Substring(0, maxLength - 3) + "...";
187+
var text = $"{result.Name} {result.Description}".ToLowerInvariant();
188+
return text.Contains(item1.ToLowerInvariant()) || text.Contains(item2.ToLowerInvariant()) ||
189+
text.Contains("compare") || text.Contains("difference") || text.Contains("versus");
368190
}
369191
}

0 commit comments

Comments
 (0)