diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs
index a606045b..01aba146 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs
@@ -71,5 +71,5 @@ public interface IConversationService
/// conversation limit
/// if pre-loading, then keys are not filter by the search query
///
- Task> GetConversationSearhKeys(string query, int convlimit = 100, int keyLimit = 10, bool preLoad = false);
+ Task> GetConversationStateSearhKeys(string query, int convlimit = 100, int keyLimit = 10, bool preLoad = false);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs
index caf58c43..a58600e5 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs
@@ -148,7 +148,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
=> throw new NotImplementedException();
List TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
=> throw new NotImplementedException();
- List GetConversationSearchKeys(int messageLimit = 2, int convlimit = 100)
+ List GetConversationStateSearchKeys(int messageLowerLimit = 2, int convUpperlimit = 100)
=> throw new NotImplementedException();
#endregion
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
index beb4e33a..63a257e5 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
@@ -222,7 +222,7 @@ public partial class ConversationService : IConversationService
_state.Save();
}
- public async Task> GetConversationSearhKeys(string query, int convlimit = 100, int keyLimit = 10, bool preLoad = false)
+ public async Task> GetConversationStateSearhKeys(string query, int convlimit = 100, int keyLimit = 10, bool preLoad = false)
{
var keys = new List();
if (!preLoad && string.IsNullOrWhiteSpace(query))
@@ -231,7 +231,7 @@ public partial class ConversationService : IConversationService
}
var db = _services.GetRequiredService();
- keys = db.GetConversationSearchKeys(convlimit: convlimit);
+ keys = db.GetConversationStateSearchKeys(convUpperlimit: convlimit);
keys = preLoad ? keys : keys.Where(x => x.Contains(query, StringComparison.OrdinalIgnoreCase)).ToList();
return keys.OrderBy(x => x).Take(keyLimit).ToList();
}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
index 4591b694..25682662 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
@@ -1,276 +1,235 @@
using BotSharp.Abstraction.Loggers.Models;
using System.IO;
-namespace BotSharp.Core.Repository
+namespace BotSharp.Core.Repository;
+
+public partial class FileRepository
{
- public partial class FileRepository
+ public void CreateNewConversation(Conversation conversation)
{
- public void CreateNewConversation(Conversation conversation)
+ var utcNow = DateTime.UtcNow;
+ conversation.CreatedTime = utcNow;
+ conversation.UpdatedTime = utcNow;
+ conversation.Tags ??= new();
+
+ var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversation.Id);
+ if (!Directory.Exists(dir))
{
- var utcNow = DateTime.UtcNow;
- conversation.CreatedTime = utcNow;
- conversation.UpdatedTime = utcNow;
- conversation.Tags ??= new();
-
- var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversation.Id);
- if (!Directory.Exists(dir))
- {
- Directory.CreateDirectory(dir);
- }
-
- var convFile = Path.Combine(dir, CONVERSATION_FILE);
- if (!File.Exists(convFile))
- {
- File.WriteAllText(convFile, JsonSerializer.Serialize(conversation, _options));
- }
-
- var dialogFile = Path.Combine(dir, DIALOG_FILE);
- if (!File.Exists(dialogFile))
- {
- File.WriteAllText(dialogFile, "[]");
- }
-
- var stateFile = Path.Combine(dir, STATE_FILE);
- if (!File.Exists(stateFile))
- {
- File.WriteAllText(stateFile, JsonSerializer.Serialize(new List(), _options));
- }
-
- var breakpointFile = Path.Combine(dir, BREAKPOINT_FILE);
- if (!File.Exists(breakpointFile))
- {
- File.WriteAllText(breakpointFile, JsonSerializer.Serialize(new List(), _options));
- }
+ Directory.CreateDirectory(dir);
}
- public bool DeleteConversations(IEnumerable conversationIds)
+ var convFile = Path.Combine(dir, CONVERSATION_FILE);
+ if (!File.Exists(convFile))
{
- if (conversationIds.IsNullOrEmpty()) return false;
-
- foreach (var conversationId in conversationIds)
- {
- var convDir = FindConversationDirectory(conversationId);
- if (string.IsNullOrEmpty(convDir)) continue;
-
- Directory.Delete(convDir, true);
- }
-
- return true;
+ File.WriteAllText(convFile, JsonSerializer.Serialize(conversation, _options));
}
- [SideCar]
- public List GetConversationDialogs(string conversationId)
+ var dialogFile = Path.Combine(dir, DIALOG_FILE);
+ if (!File.Exists(dialogFile))
{
- var dialogs = new List();
- var convDir = FindConversationDirectory(conversationId);
- if (!string.IsNullOrEmpty(convDir))
- {
- var dialogDir = Path.Combine(convDir, DIALOG_FILE);
- var texts = File.ReadAllText(dialogDir);
- try
- {
- dialogs = JsonSerializer.Deserialize>(texts, _options) ?? new List();
- }
- catch
- {
- dialogs = new List();
- }
- }
-
- return dialogs;
+ File.WriteAllText(dialogFile, "[]");
}
- [SideCar]
- public void AppendConversationDialogs(string conversationId, List dialogs)
+ var stateFile = Path.Combine(dir, STATE_FILE);
+ if (!File.Exists(stateFile))
+ {
+ File.WriteAllText(stateFile, JsonSerializer.Serialize(new List(), _options));
+ }
+
+ var breakpointFile = Path.Combine(dir, BREAKPOINT_FILE);
+ if (!File.Exists(breakpointFile))
+ {
+ File.WriteAllText(breakpointFile, JsonSerializer.Serialize(new List(), _options));
+ }
+ }
+
+ public bool DeleteConversations(IEnumerable conversationIds)
+ {
+ if (conversationIds.IsNullOrEmpty()) return false;
+
+ foreach (var conversationId in conversationIds)
{
var convDir = FindConversationDirectory(conversationId);
- if (!string.IsNullOrEmpty(convDir))
+ if (string.IsNullOrEmpty(convDir)) continue;
+
+ Directory.Delete(convDir, true);
+ }
+
+ return true;
+ }
+
+ [SideCar]
+ public List GetConversationDialogs(string conversationId)
+ {
+ var dialogs = new List();
+ var convDir = FindConversationDirectory(conversationId);
+ if (!string.IsNullOrEmpty(convDir))
+ {
+ var dialogDir = Path.Combine(convDir, DIALOG_FILE);
+ var texts = File.ReadAllText(dialogDir);
+ try
{
- var dialogFile = Path.Combine(convDir, DIALOG_FILE);
- if (File.Exists(dialogFile))
- {
- var prevDialogs = File.ReadAllText(dialogFile);
- var elements = JsonSerializer.Deserialize>(prevDialogs, _options);
- if (elements != null)
- {
- elements.AddRange(dialogs);
- }
- else
- {
- elements = elements ?? new List();
- }
-
- File.WriteAllText(dialogFile, JsonSerializer.Serialize(elements, _options));
- }
-
- 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.DialogCount += dialogs.Count();
- conv.UpdatedTime = DateTime.UtcNow;
- File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
- }
- }
+ dialogs = JsonSerializer.Deserialize>(texts, _options) ?? new List();
+ }
+ catch
+ {
+ dialogs = new List();
}
}
- public void UpdateConversationTitle(string conversationId, string title)
+ return dialogs;
+ }
+
+ [SideCar]
+ public void AppendConversationDialogs(string conversationId, List dialogs)
+ {
+ var convDir = FindConversationDirectory(conversationId);
+ if (!string.IsNullOrEmpty(convDir))
{
- var convDir = FindConversationDirectory(conversationId);
- if (!string.IsNullOrEmpty(convDir))
- {
- var convFile = Path.Combine(convDir, CONVERSATION_FILE);
- var content = File.ReadAllText(convFile);
- var record = JsonSerializer.Deserialize(content, _options);
- if (record != null)
- {
- record.Title = title;
- record.UpdatedTime = DateTime.UtcNow;
- File.WriteAllText(convFile, JsonSerializer.Serialize(record, _options));
- }
- }
- }
- public void UpdateConversationTitleAlias(string conversationId, string titleAlias)
- {
- var convDir = FindConversationDirectory(conversationId);
- if (!string.IsNullOrEmpty(convDir))
- {
- var convFile = Path.Combine(convDir, CONVERSATION_FILE);
- var content = File.ReadAllText(convFile);
- var record = JsonSerializer.Deserialize(content, _options);
- if (record != null)
- {
- record.TitleAlias = titleAlias;
- record.UpdatedTime = DateTime.UtcNow;
- File.WriteAllText(convFile, JsonSerializer.Serialize(record, _options));
- }
- }
- }
-
- public bool UpdateConversationTags(string conversationId, List tags)
- {
- if (string.IsNullOrEmpty(conversationId)) return false;
-
- var convDir = FindConversationDirectory(conversationId);
- if (string.IsNullOrEmpty(convDir)) return false;
-
- var convFile = Path.Combine(convDir, CONVERSATION_FILE);
- if (!File.Exists(convFile)) return false;
-
- var json = File.ReadAllText(convFile);
- var conv = JsonSerializer.Deserialize(json, _options);
- conv.Tags = tags ?? new();
- conv.UpdatedTime = DateTime.UtcNow;
- File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
- return true;
- }
-
- public bool AppendConversationTags(string conversationId, List tags)
- {
- if (string.IsNullOrEmpty(conversationId) || tags.IsNullOrEmpty()) return false;
-
- var convDir = FindConversationDirectory(conversationId);
- if (string.IsNullOrEmpty(convDir)) return false;
-
- var convFile = Path.Combine(convDir, CONVERSATION_FILE);
- if (!File.Exists(convFile)) return false;
-
- var json = File.ReadAllText(convFile);
- var conv = JsonSerializer.Deserialize(json, _options);
-
- var curTags = conv.Tags ?? new();
- var newTags = curTags.Concat(tags).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList();
- conv.Tags = newTags;
- conv.UpdatedTime = DateTime.UtcNow;
- File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
- return true;
- }
-
- public bool UpdateConversationMessage(string conversationId, UpdateMessageRequest request)
- {
- if (string.IsNullOrEmpty(conversationId)) return false;
-
- var dialogs = GetConversationDialogs(conversationId);
- var candidates = dialogs.Where(x => x.MetaData.MessageId == request.Message.MetaData.MessageId
- && x.MetaData.Role == request.Message.MetaData.Role).ToList();
-
- var found = candidates.Where((_, idx) => idx == request.InnderIndex).FirstOrDefault();
- if (found == null) return false;
-
- found.Content = request.Message.Content;
- found.RichContent = request.Message.RichContent;
-
- if (!string.IsNullOrEmpty(found.SecondaryContent))
- {
- found.SecondaryContent = request.Message.Content;
- }
-
- if (!string.IsNullOrEmpty(found.SecondaryRichContent))
- {
- found.SecondaryRichContent = request.Message.RichContent;
- }
-
- var convDir = FindConversationDirectory(conversationId);
- if (string.IsNullOrEmpty(convDir)) return false;
-
var dialogFile = Path.Combine(convDir, DIALOG_FILE);
- File.WriteAllText(dialogFile, JsonSerializer.Serialize(dialogs, _options));
- return true;
- }
-
- [SideCar]
- public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint)
- {
- var convDir = FindConversationDirectory(conversationId);
- if (!string.IsNullOrEmpty(convDir))
+ if (File.Exists(dialogFile))
{
- var breakpointFile = Path.Combine(convDir, BREAKPOINT_FILE);
-
- if (!File.Exists(breakpointFile))
+ var prevDialogs = File.ReadAllText(dialogFile);
+ var elements = JsonSerializer.Deserialize>(prevDialogs, _options);
+ if (elements != null)
{
- File.Create(breakpointFile);
- }
-
- var content = File.ReadAllText(breakpointFile);
- var records = JsonSerializer.Deserialize>(content, _options);
- var newBreakpoint = new List()
- {
- new ConversationBreakpoint
- {
- MessageId = breakpoint.MessageId,
- Breakpoint = breakpoint.Breakpoint,
- Reason = breakpoint.Reason,
- CreatedTime = DateTime.UtcNow,
- }
- };
-
- if (records != null && !records.IsNullOrEmpty())
- {
- records = records.Concat(newBreakpoint).ToList();
+ elements.AddRange(dialogs);
}
else
{
- records = newBreakpoint;
+ elements = elements ?? new List();
}
- File.WriteAllText(breakpointFile, JsonSerializer.Serialize(records, _options));
+ File.WriteAllText(dialogFile, JsonSerializer.Serialize(elements, _options));
+ }
+
+ 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.DialogCount += dialogs.Count();
+ conv.UpdatedTime = DateTime.UtcNow;
+ File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
+ }
}
}
+ }
- [SideCar]
- public ConversationBreakpoint? GetConversationBreakpoint(string conversationId)
+ public void UpdateConversationTitle(string conversationId, string title)
+ {
+ var convDir = FindConversationDirectory(conversationId);
+ if (!string.IsNullOrEmpty(convDir))
{
- var convDir = FindConversationDirectory(conversationId);
- if (string.IsNullOrEmpty(convDir))
+ var convFile = Path.Combine(convDir, CONVERSATION_FILE);
+ var content = File.ReadAllText(convFile);
+ var record = JsonSerializer.Deserialize(content, _options);
+ if (record != null)
{
- return null;
+ record.Title = title;
+ record.UpdatedTime = DateTime.UtcNow;
+ File.WriteAllText(convFile, JsonSerializer.Serialize(record, _options));
}
+ }
+ }
+ public void UpdateConversationTitleAlias(string conversationId, string titleAlias)
+ {
+ var convDir = FindConversationDirectory(conversationId);
+ if (!string.IsNullOrEmpty(convDir))
+ {
+ var convFile = Path.Combine(convDir, CONVERSATION_FILE);
+ var content = File.ReadAllText(convFile);
+ var record = JsonSerializer.Deserialize(content, _options);
+ if (record != null)
+ {
+ record.TitleAlias = titleAlias;
+ record.UpdatedTime = DateTime.UtcNow;
+ File.WriteAllText(convFile, JsonSerializer.Serialize(record, _options));
+ }
+ }
+ }
+ public bool UpdateConversationTags(string conversationId, List tags)
+ {
+ if (string.IsNullOrEmpty(conversationId)) return false;
+
+ var convDir = FindConversationDirectory(conversationId);
+ if (string.IsNullOrEmpty(convDir)) return false;
+
+ var convFile = Path.Combine(convDir, CONVERSATION_FILE);
+ if (!File.Exists(convFile)) return false;
+
+ var json = File.ReadAllText(convFile);
+ var conv = JsonSerializer.Deserialize(json, _options);
+ conv.Tags = tags ?? new();
+ conv.UpdatedTime = DateTime.UtcNow;
+ File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
+ return true;
+ }
+
+ public bool AppendConversationTags(string conversationId, List tags)
+ {
+ if (string.IsNullOrEmpty(conversationId) || tags.IsNullOrEmpty()) return false;
+
+ var convDir = FindConversationDirectory(conversationId);
+ if (string.IsNullOrEmpty(convDir)) return false;
+
+ var convFile = Path.Combine(convDir, CONVERSATION_FILE);
+ if (!File.Exists(convFile)) return false;
+
+ var json = File.ReadAllText(convFile);
+ var conv = JsonSerializer.Deserialize(json, _options);
+
+ var curTags = conv.Tags ?? new();
+ var newTags = curTags.Concat(tags).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList();
+ conv.Tags = newTags;
+ conv.UpdatedTime = DateTime.UtcNow;
+ File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
+ return true;
+ }
+
+ public bool UpdateConversationMessage(string conversationId, UpdateMessageRequest request)
+ {
+ if (string.IsNullOrEmpty(conversationId)) return false;
+
+ var dialogs = GetConversationDialogs(conversationId);
+ var candidates = dialogs.Where(x => x.MetaData.MessageId == request.Message.MetaData.MessageId
+ && x.MetaData.Role == request.Message.MetaData.Role).ToList();
+
+ var found = candidates.Where((_, idx) => idx == request.InnderIndex).FirstOrDefault();
+ if (found == null) return false;
+
+ found.Content = request.Message.Content;
+ found.RichContent = request.Message.RichContent;
+
+ if (!string.IsNullOrEmpty(found.SecondaryContent))
+ {
+ found.SecondaryContent = request.Message.Content;
+ }
+
+ if (!string.IsNullOrEmpty(found.SecondaryRichContent))
+ {
+ found.SecondaryRichContent = request.Message.RichContent;
+ }
+
+ var convDir = FindConversationDirectory(conversationId);
+ if (string.IsNullOrEmpty(convDir)) return false;
+
+ var dialogFile = Path.Combine(convDir, DIALOG_FILE);
+ File.WriteAllText(dialogFile, JsonSerializer.Serialize(dialogs, _options));
+ return true;
+ }
+
+ [SideCar]
+ public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint)
+ {
+ var convDir = FindConversationDirectory(conversationId);
+ if (!string.IsNullOrEmpty(convDir))
+ {
var breakpointFile = Path.Combine(convDir, BREAKPOINT_FILE);
+
if (!File.Exists(breakpointFile))
{
File.Create(breakpointFile);
@@ -278,557 +237,599 @@ namespace BotSharp.Core.Repository
var content = File.ReadAllText(breakpointFile);
var records = JsonSerializer.Deserialize>(content, _options);
-
- return records?.LastOrDefault();
- }
-
- public ConversationState GetConversationStates(string conversationId)
- {
- var states = new List();
- var convDir = FindConversationDirectory(conversationId);
- if (!string.IsNullOrEmpty(convDir))
+ var newBreakpoint = new List()
{
- var stateFile = Path.Combine(convDir, STATE_FILE);
- states = CollectConversationStates(stateFile);
- }
-
- return new ConversationState(states);
- }
-
- public void UpdateConversationStates(string conversationId, List states)
- {
- if (states.IsNullOrEmpty()) return;
-
- var convDir = FindConversationDirectory(conversationId);
- if (!string.IsNullOrEmpty(convDir))
- {
- var stateFile = Path.Combine(convDir, STATE_FILE);
- if (File.Exists(stateFile))
+ new ConversationBreakpoint
{
- var stateStr = JsonSerializer.Serialize(states, _options);
- File.WriteAllText(stateFile, stateStr);
+ MessageId = breakpoint.MessageId,
+ Breakpoint = breakpoint.Breakpoint,
+ Reason = breakpoint.Reason,
+ CreatedTime = DateTime.UtcNow,
}
- }
- }
-
- public void UpdateConversationStatus(string conversationId, string status)
- {
- var convDir = FindConversationDirectory(conversationId);
- if (!string.IsNullOrEmpty(convDir))
- {
- var convFile = Path.Combine(convDir, CONVERSATION_FILE);
- if (File.Exists(convFile))
- {
- var json = File.ReadAllText(convFile);
- var conv = JsonSerializer.Deserialize(json, _options);
- conv.Status = status;
- conv.UpdatedTime = DateTime.UtcNow;
- File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
- }
- }
- }
-
- public Conversation GetConversation(string conversationId)
- {
- var convDir = FindConversationDirectory(conversationId);
- if (string.IsNullOrEmpty(convDir)) return null;
-
- var convFile = Path.Combine(convDir, CONVERSATION_FILE);
- var content = File.ReadAllText(convFile);
- var record = JsonSerializer.Deserialize(content, _options);
-
- var dialogFile = Path.Combine(convDir, DIALOG_FILE);
- if (record != null)
- {
- record.Dialogs = CollectDialogElements(dialogFile);
- }
-
- var stateFile = Path.Combine(convDir, STATE_FILE);
- if (record != null)
- {
- var states = CollectConversationStates(stateFile);
- var curStates = new Dictionary();
- states.ForEach(x =>
- {
- curStates[x.Key] = x.Values?.LastOrDefault()?.Data ?? string.Empty;
- });
- record.States = curStates;
- }
-
- return record;
- }
-
- public PagedItems GetConversations(ConversationFilter filter)
- {
- if (filter == null)
- {
- filter = ConversationFilter.Empty();
- }
-
- var records = new List();
- var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
- var pager = filter?.Pager ?? new Pagination();
-
- if (!Directory.Exists(dir))
- {
- Directory.CreateDirectory(dir);
- }
-
- var totalDirs = Directory.GetDirectories(dir);
- foreach (var d in totalDirs)
- {
- var convFile = Path.Combine(d, CONVERSATION_FILE);
- if (!File.Exists(convFile)) continue;
-
- var json = File.ReadAllText(convFile);
- var record = JsonSerializer.Deserialize(json, _options);
- if (record == null) continue;
-
- var matched = true;
- if (filter?.Id != null)
- {
- matched = matched && record.Id == filter.Id;
- }
- if (filter?.Title != null)
- {
- matched = matched && record.Title.Contains(filter.Title);
- }
- if (filter?.TitleAlias != null)
- {
- matched = matched && record.TitleAlias.Contains(filter.TitleAlias);
- }
- if (filter?.AgentId != null)
- {
- matched = matched && record.AgentId == filter.AgentId;
- }
- if (filter?.Status != null)
- {
- matched = matched && record.Status == filter.Status;
- }
- if (filter?.Channel != null)
- {
- matched = matched && record.Channel == filter.Channel;
- }
- if (filter?.UserId != null)
- {
- matched = matched && record.UserId == filter.UserId;
- }
- if (filter?.TaskId != null)
- {
- matched = matched && record.TaskId == filter.TaskId;
- }
- if (filter?.StartTime != null)
- {
- matched = matched && record.CreatedTime >= filter.StartTime.Value;
- }
- if (filter?.Tags != null && filter.Tags.Any())
- {
- matched = matched && !record.Tags.IsNullOrEmpty() && record.Tags.Exists(t => filter.Tags.Contains(t));
- }
-
- // 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);
- }
-
- return new PagedItems
- {
- Items = records.OrderByDescending(x => x.CreatedTime).Skip(pager.Offset).Take(pager.Size),
- Count = records.Count(),
};
+
+ if (records != null && !records.IsNullOrEmpty())
+ {
+ records = records.Concat(newBreakpoint).ToList();
+ }
+ else
+ {
+ records = newBreakpoint;
+ }
+
+ File.WriteAllText(breakpointFile, JsonSerializer.Serialize(records, _options));
+ }
+ }
+
+ [SideCar]
+ public ConversationBreakpoint? GetConversationBreakpoint(string conversationId)
+ {
+ var convDir = FindConversationDirectory(conversationId);
+ if (string.IsNullOrEmpty(convDir))
+ {
+ return null;
}
- public List GetLastConversations()
+ var breakpointFile = Path.Combine(convDir, BREAKPOINT_FILE);
+ if (!File.Exists(breakpointFile))
{
- var records = new List();
- var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
-
- foreach (var d in Directory.GetDirectories(dir))
- {
- var path = Path.Combine(d, CONVERSATION_FILE);
- if (!File.Exists(path)) continue;
-
- var json = File.ReadAllText(path);
- var record = JsonSerializer.Deserialize(json, _options);
- if (record == null) continue;
-
- records.Add(record);
- }
- return records.GroupBy(r => r.UserId)
- .Select(g => g.OrderByDescending(x => x.CreatedTime).First())
- .ToList();
+ File.Create(breakpointFile);
}
- public List GetIdleConversations(int batchSize, int messageLimit, int bufferHours, IEnumerable excludeAgentIds)
+ var content = File.ReadAllText(breakpointFile);
+ var records = JsonSerializer.Deserialize>(content, _options);
+
+ return records?.LastOrDefault();
+ }
+
+ public ConversationState GetConversationStates(string conversationId)
+ {
+ var states = new List();
+ var convDir = FindConversationDirectory(conversationId);
+ if (!string.IsNullOrEmpty(convDir))
{
- var ids = new List();
- var batchLimit = 100;
- var utcNow = DateTime.UtcNow;
- var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
+ var stateFile = Path.Combine(convDir, STATE_FILE);
+ states = CollectConversationStates(stateFile);
+ }
- if (!Directory.Exists(dir))
+ return new ConversationState(states);
+ }
+
+ public void UpdateConversationStates(string conversationId, List states)
+ {
+ if (states.IsNullOrEmpty()) return;
+
+ var convDir = FindConversationDirectory(conversationId);
+ if (!string.IsNullOrEmpty(convDir))
+ {
+ var stateFile = Path.Combine(convDir, STATE_FILE);
+ if (File.Exists(stateFile))
{
- Directory.CreateDirectory(dir);
+ var stateStr = JsonSerializer.Serialize(states, _options);
+ File.WriteAllText(stateFile, stateStr);
}
+ }
+ }
- if (batchSize <= 0 || batchSize > batchLimit)
+ public void UpdateConversationStatus(string conversationId, string status)
+ {
+ var convDir = FindConversationDirectory(conversationId);
+ if (!string.IsNullOrEmpty(convDir))
+ {
+ var convFile = Path.Combine(convDir, CONVERSATION_FILE);
+ if (File.Exists(convFile))
{
- batchSize = batchLimit;
- }
-
- if (bufferHours <= 0)
- {
- bufferHours = 12;
- }
-
- if (messageLimit <= 0)
- {
- messageLimit = 2;
- }
-
- foreach (var d in Directory.GetDirectories(dir))
- {
- var convFile = Path.Combine(d, CONVERSATION_FILE);
- if (!File.Exists(convFile))
- {
- Directory.Delete(d, true);
- continue;
- }
-
var json = File.ReadAllText(convFile);
var conv = JsonSerializer.Deserialize(json, _options);
+ conv.Status = status;
+ conv.UpdatedTime = DateTime.UtcNow;
+ File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
+ }
+ }
+ }
- if (conv == null)
- {
- Directory.Delete(d, true);
- continue;
- }
+ public Conversation GetConversation(string conversationId)
+ {
+ var convDir = FindConversationDirectory(conversationId);
+ if (string.IsNullOrEmpty(convDir)) return null;
- if (conv.UpdatedTime > utcNow.AddHours(-bufferHours))
- {
- continue;
- }
+ var convFile = Path.Combine(convDir, CONVERSATION_FILE);
+ var content = File.ReadAllText(convFile);
+ var record = JsonSerializer.Deserialize(content, _options);
- if ((excludeAgentIds.Contains(conv.AgentId) && conv.DialogCount == 0)
- || (!excludeAgentIds.Contains(conv.AgentId) && conv.DialogCount <= messageLimit))
+ var dialogFile = Path.Combine(convDir, DIALOG_FILE);
+ if (record != null)
+ {
+ record.Dialogs = CollectDialogElements(dialogFile);
+ }
+
+ var stateFile = Path.Combine(convDir, STATE_FILE);
+ if (record != null)
+ {
+ var states = CollectConversationStates(stateFile);
+ var curStates = new Dictionary();
+ states.ForEach(x =>
+ {
+ curStates[x.Key] = x.Values?.LastOrDefault()?.Data ?? string.Empty;
+ });
+ record.States = curStates;
+ }
+
+ return record;
+ }
+
+ public PagedItems GetConversations(ConversationFilter filter)
+ {
+ if (filter == null)
+ {
+ filter = ConversationFilter.Empty();
+ }
+
+ var records = new List();
+ var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
+ var pager = filter?.Pager ?? new Pagination();
+
+ if (!Directory.Exists(dir))
+ {
+ Directory.CreateDirectory(dir);
+ }
+
+ var totalDirs = Directory.GetDirectories(dir);
+ foreach (var d in totalDirs)
+ {
+ var convFile = Path.Combine(d, CONVERSATION_FILE);
+ if (!File.Exists(convFile)) continue;
+
+ var json = File.ReadAllText(convFile);
+ var record = JsonSerializer.Deserialize(json, _options);
+ if (record == null) continue;
+
+ var matched = true;
+ if (filter?.Id != null)
+ {
+ matched = matched && record.Id == filter.Id;
+ }
+ if (filter?.Title != null)
+ {
+ matched = matched && record.Title.Contains(filter.Title);
+ }
+ if (filter?.TitleAlias != null)
+ {
+ matched = matched && record.TitleAlias.Contains(filter.TitleAlias);
+ }
+ if (filter?.AgentId != null)
+ {
+ matched = matched && record.AgentId == filter.AgentId;
+ }
+ if (filter?.Status != null)
+ {
+ matched = matched && record.Status == filter.Status;
+ }
+ if (filter?.Channel != null)
+ {
+ matched = matched && record.Channel == filter.Channel;
+ }
+ if (filter?.UserId != null)
+ {
+ matched = matched && record.UserId == filter.UserId;
+ }
+ if (filter?.TaskId != null)
+ {
+ matched = matched && record.TaskId == filter.TaskId;
+ }
+ if (filter?.StartTime != null)
+ {
+ matched = matched && record.CreatedTime >= filter.StartTime.Value;
+ }
+ if (filter?.Tags != null && filter.Tags.Any())
+ {
+ matched = matched && !record.Tags.IsNullOrEmpty() && record.Tags.Exists(t => filter.Tags.Contains(t));
+ }
+
+ // 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)
{
- ids.Add(conv.Id);
- if (ids.Count >= batchSize)
+ if (pair == null || string.IsNullOrWhiteSpace(pair.Key)) continue;
+
+ var foundState = convStates.FirstOrDefault(x => x.Key.IsEqualTo(pair.Key));
+ if (foundState == null)
{
- return ids;
+ matched = false;
+ break;
+ }
+
+ if (!string.IsNullOrWhiteSpace(pair.Value))
+ {
+ var curValue = foundState.Values.LastOrDefault()?.Data;
+ matched = matched && pair.Value.IsEqualTo(curValue);
}
}
}
- return ids;
+
+ if (!matched) continue;
+
+ records.Add(record);
}
-
- public List TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
+ return new PagedItems
{
- var deletedMessageIds = new List();
- if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId))
+ Items = records.OrderByDescending(x => x.CreatedTime).Skip(pager.Offset).Take(pager.Size),
+ Count = records.Count(),
+ };
+ }
+
+ public List GetLastConversations()
+ {
+ var records = new List();
+ var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
+
+ foreach (var d in Directory.GetDirectories(dir))
+ {
+ var path = Path.Combine(d, CONVERSATION_FILE);
+ if (!File.Exists(path)) continue;
+
+ var json = File.ReadAllText(path);
+ var record = JsonSerializer.Deserialize(json, _options);
+ if (record == null) continue;
+
+ records.Add(record);
+ }
+ return records.GroupBy(r => r.UserId)
+ .Select(g => g.OrderByDescending(x => x.CreatedTime).First())
+ .ToList();
+ }
+
+ public List GetIdleConversations(int batchSize, int messageLimit, int bufferHours, IEnumerable excludeAgentIds)
+ {
+ var ids = new List();
+ var batchLimit = 100;
+ var utcNow = DateTime.UtcNow;
+ var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
+
+ if (!Directory.Exists(dir))
+ {
+ Directory.CreateDirectory(dir);
+ }
+
+ if (batchSize <= 0 || batchSize > batchLimit)
+ {
+ batchSize = batchLimit;
+ }
+
+ if (bufferHours <= 0)
+ {
+ bufferHours = 12;
+ }
+
+ if (messageLimit <= 0)
+ {
+ messageLimit = 2;
+ }
+
+ foreach (var d in Directory.GetDirectories(dir))
+ {
+ var convFile = Path.Combine(d, CONVERSATION_FILE);
+ if (!File.Exists(convFile))
{
- return deletedMessageIds;
+ Directory.Delete(d, true);
+ continue;
}
- var dialogs = new List();
-
- var convDir = FindConversationDirectory(conversationId);
- if (string.IsNullOrEmpty(convDir))
+ var json = File.ReadAllText(convFile);
+ var conv = JsonSerializer.Deserialize(json, _options);
+
+ if (conv == null)
{
- return deletedMessageIds;
+ Directory.Delete(d, true);
+ continue;
}
- var dialogDir = Path.Combine(convDir, DIALOG_FILE);
- dialogs = CollectDialogElements(dialogDir);
- if (dialogs.IsNullOrEmpty())
+ if (conv.UpdatedTime > utcNow.AddHours(-bufferHours))
{
- return deletedMessageIds;
+ continue;
}
- var foundIdx = dialogs.FindIndex(x => x.MetaData?.MessageId == messageId);
- if (foundIdx < 0)
+ if ((excludeAgentIds.Contains(conv.AgentId) && conv.DialogCount == 0)
+ || (!excludeAgentIds.Contains(conv.AgentId) && conv.DialogCount <= messageLimit))
{
- return deletedMessageIds;
+ ids.Add(conv.Id);
+ if (ids.Count >= batchSize)
+ {
+ return ids;
+ }
}
+ }
+ return ids;
+ }
- deletedMessageIds = dialogs.Where((x, idx) => idx >= foundIdx && !string.IsNullOrEmpty(x.MetaData?.MessageId))
- .Select(x => x.MetaData.MessageId).Distinct().ToList();
-
- // Handle truncated dialogs
- var isSaved = HandleTruncatedDialogs(convDir, dialogDir, dialogs, foundIdx);
-
- // Handle truncated states
- var refTime = dialogs.ElementAt(foundIdx).MetaData.CreateTime;
- var stateDir = Path.Combine(convDir, STATE_FILE);
- var states = CollectConversationStates(stateDir);
- isSaved = HandleTruncatedStates(stateDir, states, messageId, refTime);
-
- // Handle truncated breakpoints
- var breakpointDir = Path.Combine(convDir, BREAKPOINT_FILE);
- var breakpoints = CollectConversationBreakpoints(breakpointDir);
- isSaved = HandleTruncatedBreakpoints(breakpointDir, breakpoints, refTime);
-
- // Remove logs
- if (cleanLog)
- {
- HandleTruncatedLogs(convDir, refTime);
- }
+ public List TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
+ {
+ var deletedMessageIds = new List();
+ if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId))
+ {
return deletedMessageIds;
}
-
- public List GetConversationSearchKeys(int messageLimit = 2, int convlimit = 100)
+ var dialogs = new List();
+
+ var convDir = FindConversationDirectory(conversationId);
+ if (string.IsNullOrEmpty(convDir))
{
- var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
- if (!Directory.Exists(dir)) return [];
-
- var count = 0;
- var keys = new List();
-
- foreach (var d in Directory.GetDirectories(dir))
- {
- var convFile = Path.Combine(d, CONVERSATION_FILE);
- var stateFile = Path.Combine(d, STATE_FILE);
- if (!File.Exists(convFile) || !File.Exists(stateFile))
- {
- continue;
- }
-
- var convJson = File.ReadAllText(convFile);
- var stateJson = File.ReadAllText(stateFile);
- var conv = JsonSerializer.Deserialize(convJson, _options);
- var states = JsonSerializer.Deserialize>(stateJson, _options);
- if (conv == null || conv.DialogCount < messageLimit)
- {
- continue;
- }
-
- var stateKeys = states?.Select(x => x.Key)?.Distinct()?.ToList() ?? [];
- keys.AddRange(stateKeys);
- count++;
-
- if (count > convlimit)
- {
- break;
- }
- }
-
- return keys.Distinct().ToList();
+ return deletedMessageIds;
}
-
- #region Private methods
- private string? FindConversationDirectory(string conversationId)
+ var dialogDir = Path.Combine(convDir, DIALOG_FILE);
+ dialogs = CollectDialogElements(dialogDir);
+ if (dialogs.IsNullOrEmpty())
{
- if (string.IsNullOrEmpty(conversationId)) return null;
-
- var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversationId);
- if (!Directory.Exists(dir)) return null;
-
- return dir;
+ return deletedMessageIds;
}
- private List CollectDialogElements(string dialogDir)
+ var foundIdx = dialogs.FindIndex(x => x.MetaData?.MessageId == messageId);
+ if (foundIdx < 0)
{
- var dialogs = new List();
-
- if (!File.Exists(dialogDir)) return dialogs;
-
- var texts = File.ReadAllText(dialogDir);
- dialogs = JsonSerializer.Deserialize>(texts) ?? new List();
- return dialogs;
+ return deletedMessageIds;
}
- private string ParseDialogElements(List dialogs)
- {
- if (dialogs.IsNullOrEmpty()) return "[]";
+ deletedMessageIds = dialogs.Where((x, idx) => idx >= foundIdx && !string.IsNullOrEmpty(x.MetaData?.MessageId))
+ .Select(x => x.MetaData.MessageId).Distinct().ToList();
- return JsonSerializer.Serialize(dialogs, _options) ?? "[]";
+ // Handle truncated dialogs
+ var isSaved = HandleTruncatedDialogs(convDir, dialogDir, dialogs, foundIdx);
+
+ // Handle truncated states
+ var refTime = dialogs.ElementAt(foundIdx).MetaData.CreateTime;
+ var stateDir = Path.Combine(convDir, STATE_FILE);
+ var states = CollectConversationStates(stateDir);
+ isSaved = HandleTruncatedStates(stateDir, states, messageId, refTime);
+
+ // Handle truncated breakpoints
+ var breakpointDir = Path.Combine(convDir, BREAKPOINT_FILE);
+ var breakpoints = CollectConversationBreakpoints(breakpointDir);
+ isSaved = HandleTruncatedBreakpoints(breakpointDir, breakpoints, refTime);
+
+ // Remove logs
+ if (cleanLog)
+ {
+ HandleTruncatedLogs(convDir, refTime);
}
- private List CollectConversationStates(string stateFile)
- {
- var states = new List();
- if (!File.Exists(stateFile)) return states;
-
- var stateStr = File.ReadAllText(stateFile);
- if (string.IsNullOrEmpty(stateStr)) return states;
-
- states = JsonSerializer.Deserialize>(stateStr, _options);
- return states ?? new List();
- }
-
- private List CollectConversationBreakpoints(string breakpointFile)
- {
- var breakpoints = new List();
- if (!File.Exists(breakpointFile)) return breakpoints;
-
- var content = File.ReadAllText(breakpointFile);
- if (string.IsNullOrEmpty(content)) return breakpoints;
-
- breakpoints = JsonSerializer.Deserialize>(content, _options);
- return breakpoints ?? new List();
- }
-
- private bool HandleTruncatedDialogs(string convDir, string dialogDir, List dialogs, int foundIdx)
- {
- var truncatedDialogs = dialogs.Where((x, idx) => idx < foundIdx).ToList();
- var isSaved = SaveTruncatedDialogs(dialogDir, truncatedDialogs);
- var convFile = Path.Combine(convDir, CONVERSATION_FILE);
- var convJson = File.ReadAllText(convFile);
- var conv = JsonSerializer.Deserialize(convJson, _options);
- if (conv != null)
- {
- conv.DialogCount = truncatedDialogs.Count;
- File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
- }
- return isSaved;
- }
-
- private bool HandleTruncatedStates(string stateDir, List states, string refMsgId, DateTime refTime)
- {
- var truncatedStates = new List();
- foreach (var state in states)
- {
- if (!state.Versioning)
- {
- truncatedStates.Add(state);
- continue;
- }
-
- var values = state.Values.Where(x => x.MessageId != refMsgId)
- .Where(x => x.UpdateTime < refTime)
- .ToList();
- if (values.Count == 0) continue;
-
- state.Values = values;
- truncatedStates.Add(state);
- }
-
- var isSaved = SaveTruncatedStates(stateDir, truncatedStates);
- return isSaved;
- }
-
- private bool HandleTruncatedBreakpoints(string breakpointDir, List breakpoints, DateTime refTime)
- {
- var truncatedBreakpoints = breakpoints?.Where(x => x.CreatedTime < refTime)?
- .ToList() ?? new List();
-
- var isSaved = SaveTruncatedBreakpoints(breakpointDir, truncatedBreakpoints);
- return isSaved;
- }
-
- private bool HandleTruncatedLogs(string convDir, DateTime refTime)
- {
- var contentLogDir = Path.Combine(convDir, "content_log");
- var stateLogDir = Path.Combine(convDir, "state_log");
-
- if (Directory.Exists(contentLogDir))
- {
- foreach (var file in Directory.GetFiles(contentLogDir))
- {
- var text = File.ReadAllText(file);
- var log = JsonSerializer.Deserialize(text);
- if (log == null) continue;
-
- if (log.CreateTime >= refTime)
- {
- File.Delete(file);
- }
- }
- }
-
- if (Directory.Exists(stateLogDir))
- {
- foreach (var file in Directory.GetFiles(stateLogDir))
- {
- var text = File.ReadAllText(file);
- var log = JsonSerializer.Deserialize(text);
- if (log == null) continue;
-
- if (log.CreateTime >= refTime)
- {
- File.Delete(file);
- }
- }
- }
-
- return true;
- }
-
- private bool SaveTruncatedDialogs(string dialogDir, List dialogs)
- {
- if (string.IsNullOrEmpty(dialogDir) || dialogs == null) return false;
- if (!File.Exists(dialogDir)) File.Create(dialogDir);
-
- var texts = ParseDialogElements(dialogs);
- File.WriteAllText(dialogDir, texts);
- return true;
- }
-
- private bool SaveTruncatedStates(string stateDir, List states)
- {
- if (string.IsNullOrEmpty(stateDir) || states == null) return false;
- if (!File.Exists(stateDir)) File.Create(stateDir);
-
- var stateStr = JsonSerializer.Serialize(states, _options);
- File.WriteAllText(stateDir, stateStr);
- return true;
- }
-
- private bool SaveTruncatedBreakpoints(string breakpointDir, List breakpoints)
- {
- if (string.IsNullOrEmpty(breakpointDir) || breakpoints == null) return false;
- if (!File.Exists(breakpointDir)) File.Create(breakpointDir);
-
- var breakpointStr = JsonSerializer.Serialize(breakpoints, _options);
- File.WriteAllText(breakpointDir, breakpointStr);
- return true;
- }
-
- private string? EncodeText(string? text)
- {
- if (string.IsNullOrEmpty(text)) return text;
-
- var bytes = Encoding.UTF8.GetBytes(text);
- var encoded = Convert.ToBase64String(bytes);
- return encoded;
- }
-
- private string? DecodeText(string? text)
- {
- if (string.IsNullOrEmpty(text)) return text;
-
- var decoded = Convert.FromBase64String(text);
- var origin = Encoding.UTF8.GetString(decoded);
- return origin;
- }
- #endregion
+ return deletedMessageIds;
}
+
+#if !DEBUG
+ [SharpCache(10)]
+#endif
+ public List GetConversationStateSearchKeys(int messageLowerLimit = 2, int convUpperlimit = 100)
+ {
+ var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
+ if (!Directory.Exists(dir)) return [];
+
+ var count = 0;
+ var keys = new List();
+
+ foreach (var d in Directory.GetDirectories(dir))
+ {
+ var convFile = Path.Combine(d, CONVERSATION_FILE);
+ var stateFile = Path.Combine(d, STATE_FILE);
+ if (!File.Exists(convFile) || !File.Exists(stateFile))
+ {
+ continue;
+ }
+
+ var convJson = File.ReadAllText(convFile);
+ var stateJson = File.ReadAllText(stateFile);
+ var conv = JsonSerializer.Deserialize(convJson, _options);
+ var states = JsonSerializer.Deserialize>(stateJson, _options);
+ if (conv == null || conv.DialogCount < messageLowerLimit)
+ {
+ continue;
+ }
+
+ var stateKeys = states?.Select(x => x.Key)?.Distinct()?.ToList() ?? [];
+ keys.AddRange(stateKeys);
+ count++;
+
+ if (count >= convUpperlimit)
+ {
+ break;
+ }
+ }
+
+ return keys.Distinct().ToList();
+ }
+
+
+ #region Private methods
+ private string? FindConversationDirectory(string conversationId)
+ {
+ if (string.IsNullOrEmpty(conversationId)) return null;
+
+ var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversationId);
+ if (!Directory.Exists(dir)) return null;
+
+ return dir;
+ }
+
+ private List CollectDialogElements(string dialogDir)
+ {
+ var dialogs = new List();
+
+ if (!File.Exists(dialogDir)) return dialogs;
+
+ var texts = File.ReadAllText(dialogDir);
+ dialogs = JsonSerializer.Deserialize>(texts) ?? new List();
+ return dialogs;
+ }
+
+ private string ParseDialogElements(List dialogs)
+ {
+ if (dialogs.IsNullOrEmpty()) return "[]";
+
+ return JsonSerializer.Serialize(dialogs, _options) ?? "[]";
+ }
+
+ private List CollectConversationStates(string stateFile)
+ {
+ var states = new List();
+ if (!File.Exists(stateFile)) return states;
+
+ var stateStr = File.ReadAllText(stateFile);
+ if (string.IsNullOrEmpty(stateStr)) return states;
+
+ states = JsonSerializer.Deserialize>(stateStr, _options);
+ return states ?? new List();
+ }
+
+ private List CollectConversationBreakpoints(string breakpointFile)
+ {
+ var breakpoints = new List();
+ if (!File.Exists(breakpointFile)) return breakpoints;
+
+ var content = File.ReadAllText(breakpointFile);
+ if (string.IsNullOrEmpty(content)) return breakpoints;
+
+ breakpoints = JsonSerializer.Deserialize>(content, _options);
+ return breakpoints ?? new List();
+ }
+
+ private bool HandleTruncatedDialogs(string convDir, string dialogDir, List dialogs, int foundIdx)
+ {
+ var truncatedDialogs = dialogs.Where((x, idx) => idx < foundIdx).ToList();
+ var isSaved = SaveTruncatedDialogs(dialogDir, truncatedDialogs);
+ var convFile = Path.Combine(convDir, CONVERSATION_FILE);
+ var convJson = File.ReadAllText(convFile);
+ var conv = JsonSerializer.Deserialize(convJson, _options);
+ if (conv != null)
+ {
+ conv.DialogCount = truncatedDialogs.Count;
+ File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options));
+ }
+ return isSaved;
+ }
+
+ private bool HandleTruncatedStates(string stateDir, List states, string refMsgId, DateTime refTime)
+ {
+ var truncatedStates = new List();
+ foreach (var state in states)
+ {
+ if (!state.Versioning)
+ {
+ truncatedStates.Add(state);
+ continue;
+ }
+
+ var values = state.Values.Where(x => x.MessageId != refMsgId)
+ .Where(x => x.UpdateTime < refTime)
+ .ToList();
+ if (values.Count == 0) continue;
+
+ state.Values = values;
+ truncatedStates.Add(state);
+ }
+
+ var isSaved = SaveTruncatedStates(stateDir, truncatedStates);
+ return isSaved;
+ }
+
+ private bool HandleTruncatedBreakpoints(string breakpointDir, List breakpoints, DateTime refTime)
+ {
+ var truncatedBreakpoints = breakpoints?.Where(x => x.CreatedTime < refTime)?
+ .ToList() ?? new List();
+
+ var isSaved = SaveTruncatedBreakpoints(breakpointDir, truncatedBreakpoints);
+ return isSaved;
+ }
+
+ private bool HandleTruncatedLogs(string convDir, DateTime refTime)
+ {
+ var contentLogDir = Path.Combine(convDir, "content_log");
+ var stateLogDir = Path.Combine(convDir, "state_log");
+
+ if (Directory.Exists(contentLogDir))
+ {
+ foreach (var file in Directory.GetFiles(contentLogDir))
+ {
+ var text = File.ReadAllText(file);
+ var log = JsonSerializer.Deserialize(text);
+ if (log == null) continue;
+
+ if (log.CreateTime >= refTime)
+ {
+ File.Delete(file);
+ }
+ }
+ }
+
+ if (Directory.Exists(stateLogDir))
+ {
+ foreach (var file in Directory.GetFiles(stateLogDir))
+ {
+ var text = File.ReadAllText(file);
+ var log = JsonSerializer.Deserialize(text);
+ if (log == null) continue;
+
+ if (log.CreateTime >= refTime)
+ {
+ File.Delete(file);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private bool SaveTruncatedDialogs(string dialogDir, List dialogs)
+ {
+ if (string.IsNullOrEmpty(dialogDir) || dialogs == null) return false;
+ if (!File.Exists(dialogDir)) File.Create(dialogDir);
+
+ var texts = ParseDialogElements(dialogs);
+ File.WriteAllText(dialogDir, texts);
+ return true;
+ }
+
+ private bool SaveTruncatedStates(string stateDir, List states)
+ {
+ if (string.IsNullOrEmpty(stateDir) || states == null) return false;
+ if (!File.Exists(stateDir)) File.Create(stateDir);
+
+ var stateStr = JsonSerializer.Serialize(states, _options);
+ File.WriteAllText(stateDir, stateStr);
+ return true;
+ }
+
+ private bool SaveTruncatedBreakpoints(string breakpointDir, List breakpoints)
+ {
+ if (string.IsNullOrEmpty(breakpointDir) || breakpoints == null) return false;
+ if (!File.Exists(breakpointDir)) File.Create(breakpointDir);
+
+ var breakpointStr = JsonSerializer.Serialize(breakpoints, _options);
+ File.WriteAllText(breakpointDir, breakpointStr);
+ return true;
+ }
+
+ private string? EncodeText(string? text)
+ {
+ if (string.IsNullOrEmpty(text)) return text;
+
+ var bytes = Encoding.UTF8.GetBytes(text);
+ var encoded = Convert.ToBase64String(bytes);
+ return encoded;
+ }
+
+ private string? DecodeText(string? text)
+ {
+ if (string.IsNullOrEmpty(text)) return text;
+
+ var decoded = Convert.FromBase64String(text);
+ var origin = Encoding.UTF8.GetString(decoded);
+ return origin;
+ }
+ #endregion
}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
index 4dd2e8f0..7461518d 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
@@ -558,7 +558,7 @@ public class ConversationController : ControllerBase
public async Task> GetConversationStateKeys([FromQuery] string query, [FromQuery] int keyLimit = 10, [FromQuery] bool preLoad = false)
{
var convService = _services.GetRequiredService();
- var keys = await convService.GetConversationSearhKeys(query, keyLimit: keyLimit, preLoad: preLoad);
+ var keys = await convService.GetConversationStateSearhKeys(query, keyLimit: keyLimit, preLoad: preLoad);
return keys;
}
#endregion
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs
index ae5082ce..cabaecd2 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs
@@ -611,13 +611,15 @@ public partial class MongoRepository
return deletedMessageIds;
}
-
- public List GetConversationSearchKeys(int messageLimit = 2, int convlimit = 100)
+#if !DEBUG
+ [SharpCache(10)]
+#endif
+ public List GetConversationStateSearchKeys(int messageLowerLimit = 2, int convUpperlimit = 100)
{
- var convFilter = Builders.Filter.Gte(x => x.DialogCount, messageLimit);
+ var convFilter = Builders.Filter.Gte(x => x.DialogCount, messageLowerLimit);
var conversations = _dc.Conversations.Find(convFilter)
.SortByDescending(x => x.UpdatedTime)
- .Limit(convlimit)
+ .Limit(convUpperlimit)
.ToList();
if (conversations.IsNullOrEmpty()) return [];