Skip to content

Commit 909b341

Browse files
authored
Merge pull request #545 from iceljc/features/add-file-handler
Features/add file handler
2 parents c080b5c + c0231f9 commit 909b341

File tree

36 files changed

+490
-268
lines changed

36 files changed

+490
-268
lines changed

BotSharp.sln

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.OpenAI", "s
9797
EndProject
9898
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.EmailHandler", "src\Plugins\BotSharp.Plugin.EmailHandler\BotSharp.Plugin.EmailHandler.csproj", "{A72B3BEB-E14B-4917-BE44-97EAE4E122D2}"
9999
EndProject
100+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.FileHandler", "src\Plugins\BotSharp.Plugin.FileHandler\BotSharp.Plugin.FileHandler.csproj", "{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}"
101+
EndProject
100102
Global
101103
GlobalSection(SolutionConfigurationPlatforms) = preSolution
102104
Debug|Any CPU = Debug|Any CPU
@@ -393,6 +395,14 @@ Global
393395
{A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|Any CPU.Build.0 = Release|Any CPU
394396
{A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|x64.ActiveCfg = Release|Any CPU
395397
{A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|x64.Build.0 = Release|Any CPU
398+
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
399+
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
400+
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|x64.ActiveCfg = Debug|Any CPU
401+
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|x64.Build.0 = Debug|Any CPU
402+
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
403+
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|Any CPU.Build.0 = Release|Any CPU
404+
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|x64.ActiveCfg = Release|Any CPU
405+
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|x64.Build.0 = Release|Any CPU
396406
EndGlobalSection
397407
GlobalSection(SolutionProperties) = preSolution
398408
HideSolutionNode = FALSE
@@ -439,6 +449,7 @@ Global
439449
{806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
440450
{6507D336-3A4D-41D4-81C0-2B900173A5FE} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
441451
{A72B3BEB-E14B-4917-BE44-97EAE4E122D2} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
452+
{D6A99D4F-6248-419E-8A43-B38ADEBABA2C} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
442453
EndGlobalSection
443454
GlobalSection(ExtensibilityGlobals) = postSolution
444455
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}

src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ namespace BotSharp.Abstraction.Files;
33
public interface IBotSharpFileService
44
{
55
string GetDirectory(string conversationId);
6-
Task<IEnumerable<MessageFileModel>> GetChatImages(string conversationId, string source, IEnumerable<string> fileTypes, List<RoleDialogModel> conversations, int? offset = null);
6+
Task<IEnumerable<MessageFileModel>> GetChatImages(string conversationId, string source,
7+
IEnumerable<RoleDialogModel> conversations, IEnumerable<string> contentTypes,
8+
bool includeScreenShot = false, int? offset = null);
79
IEnumerable<MessageFileModel> GetMessageFiles(string conversationId, IEnumerable<string> messageIds, string source, bool imageOnly = false);
810
string GetMessageFile(string conversationId, string messageId, string source, string index, string fileName);
911
IEnumerable<MessageFileModel> GetMessagesWithFile(string conversationId, IEnumerable<string> messageIds);

src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@
4848
<ItemGroup>
4949
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\agent.json" />
5050
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\generate_image.json" />
51-
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\read_file.json" />
5251
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\instruction.liquid" />
5352
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\generate_image.fn.liquid" />
54-
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\read_file.fn.liquid" />
5553
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\agent.json" />
5654
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions.json" />
5755
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions\human_intervention_needed.json" />
@@ -159,12 +157,6 @@
159157
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\instruction.liquid">
160158
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
161159
</Content>
162-
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\read_file.json">
163-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
164-
</Content>
165-
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\read_file.fn.liquid">
166-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
167-
</Content>
168160
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\generate_image.json">
169161
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
170162
</Content>

src/Infrastructure/BotSharp.Core/Files/FilePlugin.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
1717
{
1818
services.AddScoped<IBotSharpFileService, BotSharpFileService>();
1919

20-
services.AddScoped<IAgentHook, FileReaderHook>();
21-
services.AddScoped<IAgentUtilityHook, FileReaderUtilityHook>();
2220
services.AddScoped<IAgentHook, ImageGeneratorHook>();
2321
services.AddScoped<IAgentUtilityHook, ImageGeneratorUtilityHook>();
2422
}

src/Infrastructure/BotSharp.Core/Files/Hooks/FileReaderHook.cs

Lines changed: 0 additions & 52 deletions
This file was deleted.

src/Infrastructure/BotSharp.Core/Files/Hooks/FileReaderUtilityHook.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/Infrastructure/BotSharp.Core/Files/Services/BotSharpFileService.Conversation.cs

Lines changed: 114 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,43 @@
1-
using AspectInjector.Broker;
21
using BotSharp.Abstraction.Files.Converters;
32
using Microsoft.EntityFrameworkCore;
43
using System.IO;
5-
using System.Threading;
64

75
namespace BotSharp.Core.Files.Services;
86

97
public partial class BotSharpFileService
108
{
11-
public async Task<IEnumerable<MessageFileModel>> GetChatImages(string conversationId, string source, IEnumerable<string> fileTypes,
12-
List<RoleDialogModel> conversations, int? offset = null)
9+
public async Task<IEnumerable<MessageFileModel>> GetChatImages(string conversationId, string source,
10+
IEnumerable<RoleDialogModel> conversations, IEnumerable<string> contentTypes,
11+
bool includeScreenShot = false, int? offset = null)
1312
{
1413
var files = new List<MessageFileModel>();
1514
if (string.IsNullOrEmpty(conversationId) || conversations.IsNullOrEmpty())
1615
{
17-
return new List<MessageFileModel>();
16+
return files;
1817
}
1918

20-
if (offset <= 0)
21-
{
22-
offset = MIN_OFFSET;
23-
}
24-
else if (offset > MAX_OFFSET)
25-
{
26-
offset = MAX_OFFSET;
27-
}
19+
var messageIds = GetMessageIds(conversations, offset);
20+
var pathPrefix = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER);
2821

29-
var messageIds = new List<string>();
30-
if (offset.HasValue)
31-
{
32-
messageIds = conversations.Select(x => x.MessageId).Distinct().TakeLast(offset.Value).ToList();
33-
}
34-
else
35-
{
36-
messageIds = conversations.Select(x => x.MessageId).Distinct().ToList();
37-
}
38-
39-
files = await GetMessageFiles(conversationId, messageIds, source, fileTypes);
40-
return files;
41-
}
42-
43-
private async Task<List<MessageFileModel>> GetMessageFiles(string conversationId, IEnumerable<string> messageIds, string source, IEnumerable<string> fileTypes)
44-
{
45-
var files = new List<MessageFileModel>();
46-
if (string.IsNullOrEmpty(conversationId) || messageIds.IsNullOrEmpty() || fileTypes.IsNullOrEmpty()) return files;
47-
48-
var isNeedScreenShot = fileTypes.Any(x => _allowScreenShotTypes.Contains(x));
49-
var onlyScreenShot = fileTypes.All(x => _allowScreenShotTypes.Contains(x));
50-
51-
try
22+
foreach (var messageId in messageIds)
5223
{
53-
var preFixPath = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER);
24+
var dir = Path.Combine(pathPrefix, messageId, source);
25+
if (!ExistDirectory(dir)) continue;
5426

55-
foreach (var messageId in messageIds)
27+
foreach (var subDir in Directory.GetDirectories(dir))
5628
{
57-
var dir = Path.Combine(preFixPath, messageId, source);
58-
if (!ExistDirectory(dir)) continue;
29+
var file = Directory.GetFiles(subDir).FirstOrDefault();
30+
if (file == null) continue;
5931

60-
foreach (var subDir in Directory.GetDirectories(dir))
61-
{
62-
var file = Directory.GetFiles(subDir).FirstOrDefault();
63-
if (file == null) continue;
32+
var contentType = GetFileContentType(file);
33+
if (contentTypes?.Contains(contentType) != true) continue;
6434

65-
var index = subDir.Split(Path.DirectorySeparatorChar).Last();
66-
var contentType = GetFileContentType(file);
35+
var foundFiles = await GetMessageFiles(file, subDir, contentType, messageId, source, includeScreenShot);
36+
if (foundFiles.IsNullOrEmpty()) continue;
6737

68-
if ((!isNeedScreenShot || (isNeedScreenShot && !onlyScreenShot)) && _allowedImageTypes.Contains(contentType))
69-
{
70-
var model = new MessageFileModel()
71-
{
72-
MessageId = messageId,
73-
FileStorageUrl = file,
74-
ContentType = contentType
75-
};
76-
files.Add(model);
77-
}
78-
else if ((isNeedScreenShot && !onlyScreenShot || onlyScreenShot) && !_allowedImageTypes.Contains(contentType))
79-
{
80-
var screenShotDir = Path.Combine(subDir, SCREENSHOT_FILE_FOLDER);
81-
if (ExistDirectory(screenShotDir) && Directory.GetFiles(screenShotDir).Any())
82-
{
83-
foreach (var screenShot in Directory.GetFiles(screenShotDir))
84-
{
85-
contentType = GetFileContentType(screenShot);
86-
if (!_allowedImageTypes.Contains(contentType)) continue;
87-
88-
var model = new MessageFileModel()
89-
{
90-
MessageId = messageId,
91-
FileStorageUrl = screenShot,
92-
ContentType = contentType
93-
};
94-
files.Add(model);
95-
}
96-
}
97-
else
98-
{
99-
var screenShotPath = Path.Combine(subDir, SCREENSHOT_FILE_FOLDER);
100-
var images = await ConvertPdfToImages(file, screenShotPath);
101-
102-
foreach (var image in images)
103-
{
104-
contentType = GetFileContentType(image);
105-
var model = new MessageFileModel()
106-
{
107-
MessageId = messageId,
108-
FileStorageUrl = image,
109-
ContentType = contentType
110-
};
111-
files.Add(model);
112-
}
113-
}
114-
}
115-
}
38+
files.AddRange(foundFiles);
11639
}
11740
}
118-
catch (Exception ex)
119-
{
120-
_logger.LogWarning($"Error when reading conversation ({conversationId}) files: {ex.Message}\r\n{ex.InnerException}\r\n{ex.StackTrace}");
121-
}
12241

12342
return files;
12443
}
@@ -144,7 +63,7 @@ public IEnumerable<MessageFileModel> GetMessageFiles(string conversationId, IEnu
14463
foreach (var file in Directory.GetFiles(subDir))
14564
{
14665
var contentType = GetFileContentType(file);
147-
if (imageOnly && !_allowedImageTypes.Contains(contentType))
66+
if (imageOnly && !_imageTypes.Contains(contentType))
14867
{
14968
continue;
15069
}
@@ -327,6 +246,102 @@ private string GetConversationFileDirectory(string? conversationId, string? mess
327246
return dir;
328247
}
329248

249+
private IEnumerable<string> GetMessageIds(IEnumerable<RoleDialogModel> conversations, int? offset = null)
250+
{
251+
if (conversations.IsNullOrEmpty()) return Enumerable.Empty<string>();
252+
253+
if (offset <= 0)
254+
{
255+
offset = MIN_OFFSET;
256+
}
257+
else if (offset > MAX_OFFSET)
258+
{
259+
offset = MAX_OFFSET;
260+
}
261+
262+
var messageIds = new List<string>();
263+
if (offset.HasValue)
264+
{
265+
messageIds = conversations.Select(x => x.MessageId).Distinct().TakeLast(offset.Value).ToList();
266+
}
267+
else
268+
{
269+
messageIds = conversations.Select(x => x.MessageId).Distinct().ToList();
270+
}
271+
272+
return messageIds;
273+
}
274+
275+
276+
private async Task<IEnumerable<MessageFileModel>> GetMessageFiles(string file, string fileDir, string contentType,
277+
string messageId, string source, bool includeScreenShot)
278+
{
279+
var files = new List<MessageFileModel>();
280+
281+
try
282+
{
283+
if (!_imageTypes.Contains(contentType) && includeScreenShot)
284+
{
285+
var screenShotDir = Path.Combine(fileDir, SCREENSHOT_FILE_FOLDER);
286+
if (ExistDirectory(screenShotDir) && !Directory.GetFiles(screenShotDir).IsNullOrEmpty())
287+
{
288+
foreach (var screenShot in Directory.GetFiles(screenShotDir))
289+
{
290+
contentType = GetFileContentType(screenShot);
291+
if (!_imageTypes.Contains(contentType)) continue;
292+
293+
var model = new MessageFileModel()
294+
{
295+
MessageId = messageId,
296+
FileName = Path.GetFileName(screenShot),
297+
FileStorageUrl = screenShot,
298+
ContentType = contentType,
299+
FileSource = source
300+
};
301+
files.Add(model);
302+
}
303+
}
304+
else if (contentType == MediaTypeNames.Application.Pdf)
305+
{
306+
var images = await ConvertPdfToImages(file, screenShotDir);
307+
foreach (var image in images)
308+
{
309+
contentType = GetFileContentType(image);
310+
var model = new MessageFileModel()
311+
{
312+
MessageId = messageId,
313+
FileName = Path.GetFileName(image),
314+
FileStorageUrl = image,
315+
ContentType = contentType,
316+
FileSource = source
317+
};
318+
files.Add(model);
319+
}
320+
}
321+
}
322+
else
323+
{
324+
var model = new MessageFileModel()
325+
{
326+
MessageId = messageId,
327+
FileName = Path.GetFileName(file),
328+
FileStorageUrl = file,
329+
ContentType = contentType,
330+
FileSource = source
331+
};
332+
files.Add(model);
333+
}
334+
335+
return files;
336+
}
337+
catch (Exception ex)
338+
{
339+
_logger.LogWarning($"Error when getting message files {file} (messageId: {messageId}), Error: {ex.Message}\r\n{ex.InnerException}");
340+
return files;
341+
}
342+
}
343+
344+
330345
private async Task<IEnumerable<string>> ConvertPdfToImages(string pdfLoc, string imageLoc)
331346
{
332347
var converters = _services.GetServices<IPdf2ImageConverter>();

0 commit comments

Comments
 (0)