Skip to content

Commit d33bd37

Browse files
authored
Merge pull request #361 from iceljc/features/refine-conversation-functionality
Features/refine conversation functionality
2 parents b5b0279 + a918b88 commit d33bd37

File tree

9 files changed

+103
-23
lines changed

9 files changed

+103
-23
lines changed

src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public class Conversation
2424

2525
public string Channel { get; set; } = ConversationChannel.OpenAPI;
2626

27+
public int DialogCount { get; set; }
28+
2729
public DateTime UpdatedTime { get; set; } = DateTime.UtcNow;
2830
public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
2931

src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class RichContentJsonConverter : JsonConverter<IRichMessage>
1616
if (root.TryGetProperty("rich_type", out JsonElement element))
1717
{
1818
var richType = element.GetString();
19-
res = parser.ParseRichMessage(richType, jsonText, options);
19+
res = parser.ParseRichMessage(richType, jsonText, root, options);
2020
}
2121

2222
return res;

src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class TemplateMessageJsonConverter : JsonConverter<ITemplateMessage>
1616
if (root.TryGetProperty("template_type", out JsonElement element))
1717
{
1818
var templateType = element.GetString();
19-
res = parser.ParseTemplateMessage(templateType, jsonText, options);
19+
res = parser.ParseTemplateMessage(templateType, jsonText, root, options);
2020
}
2121

2222
return res;

src/Infrastructure/BotSharp.Abstraction/Messaging/MessageParser.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public MessageParser()
1212
{
1313
}
1414

15-
public IRichMessage? ParseRichMessage(string richType, string jsonText, JsonSerializerOptions options)
15+
public IRichMessage? ParseRichMessage(string richType, string jsonText, JsonElement root, JsonSerializerOptions options)
1616
{
1717
IRichMessage? res = null;
1818

@@ -36,11 +36,22 @@ public MessageParser()
3636
{
3737
res = JsonSerializer.Deserialize<TextMessage>(jsonText, options);
3838
}
39+
else if (richType == RichTypeEnum.GenericTemplate)
40+
{
41+
if (root.TryGetProperty("element_type", out var element))
42+
{
43+
var elementType = element.GetString();
44+
if (elementType == typeof(GenericElement).Name)
45+
{
46+
res = JsonSerializer.Deserialize<GenericTemplateMessage<GenericElement>>(jsonText, options);
47+
}
48+
}
49+
}
3950

4051
return res;
4152
}
4253

43-
public ITemplateMessage? ParseTemplateMessage(string templateType, string jsonText, JsonSerializerOptions options)
54+
public ITemplateMessage? ParseTemplateMessage(string templateType, string jsonText, JsonElement root, JsonSerializerOptions options)
4455
{
4556
ITemplateMessage? res = null;
4657

@@ -60,6 +71,17 @@ public MessageParser()
6071
{
6172
res = JsonSerializer.Deserialize<ProductTemplateMessage>(jsonText, options);
6273
}
74+
else if (templateType == TemplateTypeEnum.Generic)
75+
{
76+
if (root.TryGetProperty("element_type", out var element))
77+
{
78+
var elementType = element.GetString();
79+
if (elementType == typeof(GenericElement).Name)
80+
{
81+
res = JsonSerializer.Deserialize<GenericTemplateMessage<GenericElement>>(jsonText, options);
82+
}
83+
}
84+
}
6385

6486
return res;
6587
}

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void Append(string conversationId, RoleDialogModel dialog)
4646
AgentId = agentId,
4747
MessageId = dialog.MessageId,
4848
FunctionName = dialog.FunctionName,
49-
CreateTime = dialog.CreatedAt
49+
CreateTime = DateTime.UtcNow
5050
};
5151

5252
var content = dialog.Content.RemoveNewLine();
@@ -65,7 +65,7 @@ public void Append(string conversationId, RoleDialogModel dialog)
6565
MessageId = dialog.MessageId,
6666
SenderId = dialog.SenderId,
6767
FunctionName = dialog.FunctionName,
68-
CreateTime = dialog.CreatedAt
68+
CreateTime = DateTime.UtcNow
6969
};
7070

7171
var content = dialog.Content.RemoveNewLine();

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ public void AppendConversationDialogs(string conversationId, List<DialogElement>
117117
var conv = JsonSerializer.Deserialize<Conversation>(json, _options);
118118
if (conv != null)
119119
{
120+
conv.DialogCount += dialogs.Count();
120121
conv.UpdatedTime = DateTime.UtcNow;
121122
File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
122123
}
@@ -353,23 +354,40 @@ public List<string> GetIdleConversations(int batchSize, int messageLimit, int bu
353354
batchSize = batchLimit;
354355
}
355356

357+
if (bufferHours <= 0)
358+
{
359+
bufferHours = 12;
360+
}
361+
362+
if (messageLimit <= 0)
363+
{
364+
messageLimit = 2;
365+
}
366+
356367
foreach (var d in Directory.GetDirectories(dir))
357368
{
358369
var convFile = Path.Combine(d, CONVERSATION_FILE);
359370
if (!File.Exists(convFile))
360371
{
372+
Directory.Delete(d, true);
361373
continue;
362374
}
363375

364376
var json = File.ReadAllText(convFile);
365377
var conv = JsonSerializer.Deserialize<Conversation>(json, _options);
366-
if (conv == null || conv.UpdatedTime > utcNow.AddHours(-bufferHours))
378+
379+
if (conv == null)
367380
{
381+
Directory.Delete(d, true);
368382
continue;
369383
}
370384

371-
var dialogs = GetConversationDialogs(conv.Id);
372-
if (dialogs.Count <= messageLimit)
385+
if (conv.UpdatedTime > utcNow.AddHours(-bufferHours))
386+
{
387+
continue;
388+
}
389+
390+
if (conv.DialogCount <= messageLimit)
373391
{
374392
ids.Add(conv.Id);
375393
if (ids.Count >= batchSize)
@@ -398,7 +416,7 @@ public bool TruncateConversation(string conversationId, string messageId, bool c
398416
if (foundIdx < 0) return false;
399417

400418
// Handle truncated dialogs
401-
var isSaved = HandleTruncatedDialogs(dialogDir, dialogs, foundIdx);
419+
var isSaved = HandleTruncatedDialogs(convDir, dialogDir, dialogs, foundIdx);
402420
if (!isSaved) return false;
403421

404422
// Handle truncated states
@@ -489,10 +507,18 @@ private List<StateKeyValue> CollectConversationStates(string stateFile)
489507
return states ?? new List<StateKeyValue>();
490508
}
491509

492-
private bool HandleTruncatedDialogs(string dialogDir, List<DialogElement> dialogs, int foundIdx)
510+
private bool HandleTruncatedDialogs(string convDir, string dialogDir, List<DialogElement> dialogs, int foundIdx)
493511
{
494512
var truncatedDialogs = dialogs.Where((x, idx) => idx < foundIdx).ToList();
495513
var isSaved = SaveTruncatedDialogs(dialogDir, truncatedDialogs);
514+
var convFile = Path.Combine(convDir, CONVERSATION_FILE);
515+
var convJson = File.ReadAllText(convFile);
516+
var conv = JsonSerializer.Deserialize<Conversation>(convJson, _options);
517+
if (conv != null)
518+
{
519+
conv.DialogCount = truncatedDialogs.Count;
520+
File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
521+
}
496522
return isSaved;
497523
}
498524

src/Infrastructure/BotSharp.OpenAPI/BackgroundServices/ConversationTimeoutService.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,18 @@ public ConversationTimeoutService(IServiceProvider services, ILogger<Conversatio
1515

1616
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
1717
{
18-
_logger.LogInformation("Conversation Timeout Service is running.");
18+
_logger.LogInformation("Conversation Timeout Service is running...");
19+
20+
_ = Task.Run(async () =>
21+
{
22+
await DoWork(stoppingToken);
23+
});
24+
}
25+
26+
private async Task DoWork(CancellationToken stoppingToken)
27+
{
28+
_logger.LogInformation("Conversation Timeout Service is doing work...");
29+
1930
try
2031
{
2132
while (true)

src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public class ConversationDocument : MongoBase
88
public string Title { get; set; }
99
public string Channel { get; set; }
1010
public string Status { get; set; }
11+
public int DialogCount { get; set; }
1112
public DateTime CreatedTime { get; set; }
1213
public DateTime UpdatedTime { get; set; }
1314
public DateTime Breakpoint { get; set; }

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ public void AppendConversationDialogs(string conversationId, List<DialogElement>
123123
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
124124
var dialogElements = dialogs.Select(x => DialogMongoElement.ToMongoElement(x)).ToList();
125125
var updateDialog = Builders<ConversationDialogDocument>.Update.PushEach(x => x.Dialogs, dialogElements);
126-
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow);
126+
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow)
127+
.Inc(x => x.DialogCount, dialogs.Count);
127128

128129
_dc.ConversationDialogs.UpdateOne(filterDialog, updateDialog);
129130
_dc.Conversations.UpdateOne(filterConv, updateConv);
@@ -168,13 +169,16 @@ public ConversationState GetConversationStates(string conversationId)
168169

169170
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
170171
{
171-
if (string.IsNullOrEmpty(conversationId) || states.IsNullOrEmpty()) return;
172+
if (string.IsNullOrEmpty(conversationId) || states == null) return;
172173

174+
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
173175
var filterStates = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
174176
var saveStates = states.Select(x => StateMongoElement.ToMongoElement(x)).ToList();
175177
var updateStates = Builders<ConversationStateDocument>.Update.Set(x => x.States, saveStates);
178+
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow);
176179

177180
_dc.ConversationStates.UpdateOne(filterStates, updateStates);
181+
_dc.Conversations.UpdateOne(filterConv, updateConv);
178182
}
179183

180184
public void UpdateConversationStatus(string conversationId, string status)
@@ -220,6 +224,7 @@ public Conversation GetConversation(string conversationId)
220224
Status = conv.Status,
221225
Dialogs = dialogElements,
222226
States = curStates,
227+
DialogCount = conv.DialogCount,
223228
CreatedTime = conv.CreatedTime,
224229
UpdatedTime = conv.UpdatedTime,
225230
Breakpoint = conv.Breakpoint
@@ -308,6 +313,7 @@ public PagedItems<Conversation> GetConversations(ConversationFilter filter)
308313
Title = conv.Title,
309314
Channel = conv.Channel,
310315
Status = conv.Status,
316+
DialogCount = conv.DialogCount,
311317
CreatedTime = conv.CreatedTime,
312318
UpdatedTime = conv.UpdatedTime,
313319
Breakpoint = conv.Breakpoint
@@ -335,6 +341,7 @@ public List<Conversation> GetLastConversations()
335341
Title = c.Title,
336342
Channel = c.Channel,
337343
Status = c.Status,
344+
DialogCount = c.DialogCount,
338345
CreatedTime = c.CreatedTime,
339346
UpdatedTime = c.UpdatedTime,
340347
Breakpoint = c.Breakpoint
@@ -353,11 +360,21 @@ public List<string> GetIdleConversations(int batchSize, int messageLimit, int bu
353360
batchSize = batchLimit;
354361
}
355362

363+
if (bufferHours <= 0)
364+
{
365+
bufferHours = 12;
366+
}
367+
368+
if (messageLimit <= 0)
369+
{
370+
messageLimit = 2;
371+
}
372+
356373
while (true)
357374
{
358375
var skip = (page - 1) * batchSize;
359376
var candidates = _dc.Conversations.AsQueryable()
360-
.Where(x => x.UpdatedTime <= utcNow.AddHours(-bufferHours))
377+
.Where(x => (x.DialogCount <= messageLimit) && x.UpdatedTime <= utcNow.AddHours(-bufferHours))
361378
.Skip(skip)
362379
.Take(batchSize)
363380
.Select(x => x.Id)
@@ -367,13 +384,8 @@ public List<string> GetIdleConversations(int batchSize, int messageLimit, int bu
367384
{
368385
break;
369386
}
370-
371-
var targets = _dc.ConversationDialogs.AsQueryable()
372-
.Where(x => candidates.Contains(x.ConversationId) && x.Dialogs != null && x.Dialogs.Count <= messageLimit)
373-
.Select(x => x.ConversationId)
374-
.ToList();
375-
376-
conversationIds = conversationIds.Concat(targets).ToList();
387+
388+
conversationIds = conversationIds.Concat(candidates).Distinct().ToList();
377389
if (conversationIds.Count >= batchSize)
378390
{
379391
break;
@@ -420,10 +432,16 @@ public bool TruncateConversation(string conversationId, string messageId, bool c
420432
_dc.ConversationStates.ReplaceOne(stateFilter, foundStates);
421433
}
422434

423-
// Save
435+
// Save dialogs
424436
foundDialog.Dialogs = truncatedDialogs;
425437
_dc.ConversationDialogs.ReplaceOne(dialogFilter, foundDialog);
426438

439+
// Update conversation
440+
var convFilter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
441+
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow)
442+
.Set(x => x.DialogCount, truncatedDialogs.Count);
443+
_dc.Conversations.UpdateOne(convFilter, updateConv);
444+
427445
// Remove logs
428446
if (cleanLog)
429447
{

0 commit comments

Comments
 (0)