From 0d91a2b558f3e9668fb326ff5b29ed4008011490 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Fri, 8 Mar 2024 16:02:08 -0600
Subject: [PATCH] add conv state filter
---
.../Filters/ConversationFilter.cs | 11 ++++
.../FileRepository.Conversation.cs | 50 ++++++++++++++++---
.../ConversationTimeoutService.cs | 3 +-
.../Controllers/ConversationController.cs | 4 +-
.../MongoRepository.Conversation.cs | 37 ++++++++++++--
5 files changed, 90 insertions(+), 15 deletions(-)
diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs
index 33571223..432b34bb 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs
@@ -16,4 +16,15 @@ public class ConversationFilter
/// Agent task id
///
public string? TaskId { get; set; }
+
+ ///
+ /// Check whether each key in the list is in the conversation states and its value equals to target value if not empty
+ ///
+ public IEnumerable States { get; set; } = new List();
}
+
+public class KeyValue
+{
+ public string Key { get; set; }
+ public string? Value { get; set; }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
index ab587c14..49d695f0 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
@@ -103,11 +103,23 @@ namespace BotSharp.Core.Repository
var convDir = FindConversationDirectory(conversationId);
if (!string.IsNullOrEmpty(convDir))
{
- var dialogDir = Path.Combine(convDir, DIALOG_FILE);
- if (File.Exists(dialogDir))
+ var dialogFile = Path.Combine(convDir, DIALOG_FILE);
+ if (File.Exists(dialogFile))
{
var texts = ParseDialogElements(dialogs);
- File.AppendAllLines(dialogDir, texts);
+ File.AppendAllLines(dialogFile, texts);
+ }
+
+ var convFile = Path.Combine(convDir, CONVERSATION_FILE);
+ if (File.Exists(convFile))
+ {
+ var json = File.ReadAllText(convFile);
+ var conv = JsonSerializer.Deserialize(json, _options);
+ if (conv != null)
+ {
+ conv.UpdatedTime = DateTime.UtcNow;
+ File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
+ }
}
}
}
@@ -219,10 +231,10 @@ namespace BotSharp.Core.Repository
var totalDirs = Directory.GetDirectories(dir);
foreach (var d in totalDirs)
{
- var path = Path.Combine(d, CONVERSATION_FILE);
- if (!File.Exists(path)) continue;
+ var convFile = Path.Combine(d, CONVERSATION_FILE);
+ if (!File.Exists(convFile)) continue;
- var json = File.ReadAllText(path);
+ var json = File.ReadAllText(convFile);
var record = JsonSerializer.Deserialize(json, _options);
if (record == null) continue;
@@ -234,6 +246,30 @@ namespace BotSharp.Core.Repository
if (filter?.UserId != null) matched = matched && record.UserId == filter.UserId;
if (filter?.TaskId != null) matched = matched && record.TaskId == filter.TaskId;
+ // Check states
+ if (filter != null && !filter.States.IsNullOrEmpty())
+ {
+ var stateFile = Path.Combine(d, STATE_FILE);
+ var convStates = CollectConversationStates(stateFile);
+ foreach (var pair in filter.States)
+ {
+ if (pair == null || string.IsNullOrWhiteSpace(pair.Key)) continue;
+
+ var foundState = convStates.FirstOrDefault(x => x.Key.IsEqualTo(pair.Key));
+ if (foundState == null)
+ {
+ matched = false;
+ break;
+ }
+
+ if (!string.IsNullOrWhiteSpace(pair.Value))
+ {
+ var curValue = foundState.Values.LastOrDefault()?.Data;
+ matched = matched && pair.Value.IsEqualTo(curValue);
+ }
+ }
+ }
+
if (!matched) continue;
records.Add(record);
}
@@ -288,7 +324,7 @@ namespace BotSharp.Core.Repository
var json = File.ReadAllText(convFile);
var conv = JsonSerializer.Deserialize(json, _options);
- if (conv == null || conv.CreatedTime > utcNow.AddHours(-bufferHours))
+ if (conv == null || conv.UpdatedTime > utcNow.AddHours(-bufferHours))
{
continue;
}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/BackgroundServices/ConversationTimeoutService.cs b/src/Infrastructure/BotSharp.OpenAPI/BackgroundServices/ConversationTimeoutService.cs
index fe149ff0..3f453485 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/BackgroundServices/ConversationTimeoutService.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/BackgroundServices/ConversationTimeoutService.cs
@@ -24,9 +24,8 @@ namespace BotSharp.OpenAPI.BackgroundServices
var delay = Task.Delay(TimeSpan.FromHours(1));
try
{
- await CloseIdleConversationsAsync(TimeSpan.FromMinutes(10));
await CleanIdleConversationsAsync();
-
+ await CloseIdleConversationsAsync(TimeSpan.FromMinutes(10));
}
catch (Exception ex)
{
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
index 92a17d63..fee57e18 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
@@ -33,8 +33,8 @@ public class ConversationController : ControllerBase
return ConversationViewModel.FromSession(conv);
}
- [HttpGet("/conversations")]
- public async Task> GetConversations([FromQuery] ConversationFilter filter)
+ [HttpPost("/conversations")]
+ public async Task> GetConversations([FromBody] ConversationFilter filter)
{
var service = _services.GetRequiredService();
var conversations = await service.GetConversations(filter);
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs
index 047b7884..968c6b2c 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs
@@ -3,6 +3,7 @@ using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Repositories.Models;
using BotSharp.Plugin.MongoStorage.Collections;
using BotSharp.Plugin.MongoStorage.Models;
+using System.Text.RegularExpressions;
namespace BotSharp.Plugin.MongoStorage.Repository;
@@ -224,6 +225,35 @@ public partial class MongoRepository
if (!string.IsNullOrEmpty(filter.UserId)) filters.Add(builder.Eq(x => x.UserId, filter.UserId));
if (!string.IsNullOrEmpty(filter.TaskId)) filters.Add(builder.Eq(x => x.TaskId, filter.TaskId));
+ // Check states
+ if (!filter.States.IsNullOrEmpty())
+ {
+ var targetConvIds = new List();
+
+ foreach (var pair in filter.States)
+ {
+ if (pair == null || string.IsNullOrWhiteSpace(pair.Key)) continue;
+
+ var query = _dc.ConversationStates.AsQueryable();
+ var convIds = query.AsEnumerable().Where(x =>
+ {
+ var foundState = x.States.FirstOrDefault(s => s.Key.IsEqualTo(pair.Key));
+ if (foundState == null) return false;
+
+ if (!string.IsNullOrWhiteSpace(pair.Value))
+ {
+ return pair.Value.IsEqualTo(foundState.Values.LastOrDefault()?.Data);
+ }
+
+ return true;
+ }).Select(x => x.ConversationId).ToList();
+
+ targetConvIds = targetConvIds.Concat(convIds).Distinct().ToList();
+ }
+
+ filters.Add(builder.In(x => x.Id, targetConvIds));
+ }
+
var filterDef = builder.And(filters);
var sortDef = Builders.Sort.Descending(x => x.CreatedTime);
var pager = filter?.Pager ?? new Pagination();
@@ -258,7 +288,7 @@ public partial class MongoRepository
{
var records = new List();
var conversations = _dc.Conversations.Aggregate()
- .Group(c => c.UserId, g => g.OrderByDescending(x => x.CreatedTime).First())
+ .Group(c => c.UserId, g => g.First(x => x.CreatedTime == g.Select(y => y.CreatedTime).Max()))
.ToList();
return conversations.Select(c => new Conversation()
{
@@ -276,7 +306,6 @@ public partial class MongoRepository
public List GetIdleConversations(int batchSize, int messageLimit, int bufferHours)
{
var page = 1;
- var pageLimit = 10;
var batchLimit = 50;
var utcNow = DateTime.UtcNow;
var conversationIds = new List();
@@ -286,11 +315,11 @@ public partial class MongoRepository
batchSize = batchLimit;
}
- while (true && page < pageLimit)
+ while (true)
{
var skip = (page - 1) * batchSize;
var candidates = _dc.Conversations.AsQueryable()
- .Where(x => x.CreatedTime <= utcNow.AddHours(-bufferHours))
+ .Where(x => x.UpdatedTime <= utcNow.AddHours(-bufferHours))
.Skip(skip)
.Take(batchSize)
.Select(x => x.Id)