using BotSharp.Abstraction.Routing; namespace BotSharp.Plugin.FileHandler.Functions; public class ReadPdfFn : IFunctionCallback { public string Name => "util-file-read_pdf"; public string Indication => "Reading pdf"; private readonly IServiceProvider _services; private readonly ILogger _logger; private readonly IEnumerable _pdfContentTypes = new List { MediaTypeNames.Application.Pdf }; public ReadPdfFn( IServiceProvider services, ILogger logger) { _services = services; _logger = logger; } public async Task Execute(RoleDialogModel message) { var args = JsonSerializer.Deserialize(message.FunctionArgs); var conv = _services.GetRequiredService(); var routingCtx = _services.GetRequiredService(); var agentService = _services.GetRequiredService(); Agent? fromAgent = null; if (!string.IsNullOrEmpty(message.CurrentAgentId)) { fromAgent = await agentService.GetAgent(message.CurrentAgentId); } var agent = new Agent { Id = fromAgent?.Id ?? BuiltInAgentId.UtilityAssistant, Name = fromAgent?.Name ?? "Utility Assistant", Instruction = fromAgent?.Instruction ?? args?.UserRequest ?? "Please describe the pdf file(s).", LlmConfig = fromAgent?.LlmConfig ?? new() }; var wholeDialogs = routingCtx.GetDialogs(); if (wholeDialogs.IsNullOrEmpty()) { wholeDialogs = conv.GetDialogHistory(); } var dialogs = await AssembleFiles(conv.ConversationId, wholeDialogs); var response = await GetChatCompletion(agent, dialogs); message.Content = response; return true; } private async Task> AssembleFiles(string conversationId, List dialogs) { if (dialogs.IsNullOrEmpty()) { return new List(); } var fileStorage = _services.GetRequiredService(); var messageIds = dialogs.Select(x => x.MessageId).Distinct().ToList(); var screenshots = await fileStorage.GetMessageFileScreenshotsAsync(conversationId, messageIds); if (screenshots.IsNullOrEmpty()) return dialogs; foreach (var dialog in dialogs) { var found = screenshots.Where(x => x.MessageId == dialog.MessageId).ToList(); if (found.IsNullOrEmpty()) continue; dialog.Files = found.Select(x => new BotSharpFile { ContentType = x.ContentType, FileUrl = x.FileUrl, FileStorageUrl = x.FileStorageUrl }).ToList(); } return dialogs; } private async Task GetChatCompletion(Agent agent, List dialogs) { try { var (provider, model) = GetLlmProviderModel(); var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model); var response = await completion.GetChatCompletions(agent, dialogs); return response.Content; } catch (Exception ex) { var error = $"Error when analyzing pdf file(s)."; _logger.LogWarning(ex, $"{error}"); return error; } } private (string, string) GetLlmProviderModel() { var state = _services.GetRequiredService(); var llmProviderService = _services.GetRequiredService(); var provider = state.GetState("image_read_llm_provider"); var model = state.GetState("image_read_llm_model"); if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(model)) { return (provider, model); } provider = "openai"; model = "gpt-5-mini"; var models = llmProviderService.GetProviderModels(provider); var foundModel = models.FirstOrDefault(x => x.Image?.Reading?.IsDefault == true) ?? models.FirstOrDefault(x => x.Image?.Reading != null); model = foundModel?.Name ?? model; return (provider, model); } }