| | | 1 | | using Services.ApiServices.OpenRouterApi.Models; |
| | | 2 | | |
| | | 3 | | namespace Services.AiAssistant; |
| | | 4 | | |
| | | 5 | | /// <summary> |
| | | 6 | | /// Агрегирует все <see cref="IAiActionSuggestor"/> и возвращает дедуплицированный список чипов. |
| | | 7 | | /// Порядок: read-чипы первыми, nav-чипы после. |
| | | 8 | | /// </summary> |
| | | 9 | | public sealed class AiActionSuggestorAggregator |
| | | 10 | | { |
| | | 11 | | private readonly IEnumerable<IAiActionSuggestor> _suggestors; |
| | | 12 | | |
| | 10 | 13 | | public AiActionSuggestorAggregator(IEnumerable<IAiActionSuggestor> suggestors) |
| | 10 | 14 | | => _suggestors = suggestors; |
| | | 15 | | |
| | | 16 | | public List<AiActionLinkDto> Suggest(AiSuggestionContext ctx) |
| | | 17 | | { |
| | 10 | 18 | | var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase); |
| | 10 | 19 | | var read = new List<AiActionLinkDto>(); |
| | 10 | 20 | | var nav = new List<AiActionLinkDto>(); |
| | | 21 | | |
| | 40 | 22 | | foreach (var suggestor in _suggestors) |
| | | 23 | | { |
| | 56 | 24 | | foreach (var link in suggestor.Suggest(ctx)) |
| | | 25 | | { |
| | 18 | 26 | | var key = $"{link.Kind}|{link.Url}|{link.ChatMessage}"; |
| | 18 | 27 | | if (!seen.Add(key)) continue; |
| | | 28 | | |
| | 20 | 29 | | if (link.IsNav) nav.Add(link); |
| | 10 | 30 | | else read.Add(link); |
| | | 31 | | } |
| | | 32 | | } |
| | | 33 | | |
| | 10 | 34 | | return [.. read, .. nav]; |
| | | 35 | | } |
| | | 36 | | } |