commit
23483f263b
|
|
@ -63,4 +63,13 @@ public interface IConversationService
|
||||||
bool IsConversationMode();
|
bool IsConversationMode();
|
||||||
|
|
||||||
void SaveStates();
|
void SaveStates();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get conversation keys for searching
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">search query</param>
|
||||||
|
/// <param name="convLimit">conversation limit</param>
|
||||||
|
/// <param name="preLoad">if pre-loading, then keys are not filter by the search query</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<string>> GetConversationSearhKeys(string query, int convlimit = 100, int keyLimit = 10, bool preLoad = false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,9 @@ public interface IBotSharpRepository : IHaveServiceProvider
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
List<string> GetIdleConversations(int batchSize, int messageLimit, int bufferHours, IEnumerable<string> excludeAgentIds)
|
List<string> GetIdleConversations(int batchSize, int messageLimit, int bufferHours, IEnumerable<string> excludeAgentIds)
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
IEnumerable<string> TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
|
List<string> TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
List<string> GetConversationSearchKeys(int messageLimit = 2, int convlimit = 100)
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -221,4 +221,18 @@ public partial class ConversationService : IConversationService
|
||||||
{
|
{
|
||||||
_state.Save();
|
_state.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<string>> GetConversationSearhKeys(string query, int convlimit = 100, int keyLimit = 10, bool preLoad = false)
|
||||||
|
{
|
||||||
|
var keys = new List<string>();
|
||||||
|
if (!preLoad && string.IsNullOrWhiteSpace(query))
|
||||||
|
{
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||||
|
keys = db.GetConversationSearchKeys(convlimit: convlimit);
|
||||||
|
keys = preLoad ? keys : keys.Where(x => x.Contains(query, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||||
|
return keys.Take(keyLimit).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ public class BotSharpDbContext : Database, IBotSharpRepository
|
||||||
public void UpdateConversationStatus(string conversationId, string status)
|
public void UpdateConversationStatus(string conversationId, string status)
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
|
|
||||||
public IEnumerable<string> TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
|
public List<string> TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -547,7 +547,7 @@ namespace BotSharp.Core.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public IEnumerable<string> TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
|
public List<string> TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
|
||||||
{
|
{
|
||||||
var deletedMessageIds = new List<string>();
|
var deletedMessageIds = new List<string>();
|
||||||
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId))
|
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId))
|
||||||
|
|
@ -603,6 +603,46 @@ namespace BotSharp.Core.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<string> GetConversationSearchKeys(int messageLimit = 2, int convlimit = 100)
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
|
||||||
|
if (!Directory.Exists(dir)) return [];
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
var keys = new List<string>();
|
||||||
|
|
||||||
|
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<Conversation>(convJson, _options);
|
||||||
|
var states = JsonSerializer.Deserialize<List<StateKeyValue>>(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#region Private methods
|
#region Private methods
|
||||||
private string? FindConversationDirectory(string conversationId)
|
private string? FindConversationDirectory(string conversationId)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -553,6 +553,16 @@ public class ConversationController : ControllerBase
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Search state keys
|
||||||
|
[HttpGet("/conversation/state/keys")]
|
||||||
|
public async Task<List<string>> GetConversationStateKeys([FromQuery] string query, [FromQuery] int keyLimit = 10, [FromQuery] bool preLoad = false)
|
||||||
|
{
|
||||||
|
var convService = _services.GetRequiredService<IConversationService>();
|
||||||
|
var keys = await convService.GetConversationSearhKeys(query, keyLimit: keyLimit, preLoad: preLoad);
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Private methods
|
#region Private methods
|
||||||
private void SetStates(IConversationService conv, NewMessageModel input)
|
private void SetStates(IConversationService conv, NewMessageModel input)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,6 @@ public class ConversationDialogDocument : MongoBase
|
||||||
{
|
{
|
||||||
public string ConversationId { get; set; }
|
public string ConversationId { get; set; }
|
||||||
public string AgentId { get; set; }
|
public string AgentId { get; set; }
|
||||||
|
public DateTime UpdatedTime { get; set; }
|
||||||
public List<DialogMongoElement> Dialogs { get; set; }
|
public List<DialogMongoElement> Dialogs { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ public class ConversationStateDocument : MongoBase
|
||||||
{
|
{
|
||||||
public string ConversationId { get; set; }
|
public string ConversationId { get; set; }
|
||||||
public string AgentId { get; set; }
|
public string AgentId { get; set; }
|
||||||
|
public DateTime UpdatedTime { get; set; }
|
||||||
public List<StateMongoElement> States { get; set; } = new List<StateMongoElement>();
|
public List<StateMongoElement> States { get; set; } = new List<StateMongoElement>();
|
||||||
public List<BreakpointMongoElement> Breakpoints { get; set; } = new List<BreakpointMongoElement>();
|
public List<BreakpointMongoElement> Breakpoints { get; set; } = new List<BreakpointMongoElement>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,8 @@ public partial class MongoRepository
|
||||||
Id = Guid.NewGuid().ToString(),
|
Id = Guid.NewGuid().ToString(),
|
||||||
ConversationId = convDoc.Id,
|
ConversationId = convDoc.Id,
|
||||||
AgentId = conversation.AgentId,
|
AgentId = conversation.AgentId,
|
||||||
Dialogs = new List<DialogMongoElement>()
|
Dialogs = [],
|
||||||
|
UpdatedTime = utcNow
|
||||||
};
|
};
|
||||||
|
|
||||||
var stateDoc = new ConversationStateDocument
|
var stateDoc = new ConversationStateDocument
|
||||||
|
|
@ -38,8 +39,9 @@ public partial class MongoRepository
|
||||||
Id = Guid.NewGuid().ToString(),
|
Id = Guid.NewGuid().ToString(),
|
||||||
ConversationId = convDoc.Id,
|
ConversationId = convDoc.Id,
|
||||||
AgentId = conversation.AgentId,
|
AgentId = conversation.AgentId,
|
||||||
States = new List<StateMongoElement>(),
|
States = [],
|
||||||
Breakpoints = new List<BreakpointMongoElement>()
|
Breakpoints = [],
|
||||||
|
UpdatedTime = utcNow
|
||||||
};
|
};
|
||||||
|
|
||||||
_dc.Conversations.InsertOne(convDoc);
|
_dc.Conversations.InsertOne(convDoc);
|
||||||
|
|
@ -97,7 +99,8 @@ public partial class MongoRepository
|
||||||
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
||||||
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
var dialogElements = dialogs.Select(x => DialogMongoElement.ToMongoElement(x)).ToList();
|
var dialogElements = dialogs.Select(x => DialogMongoElement.ToMongoElement(x)).ToList();
|
||||||
var updateDialog = Builders<ConversationDialogDocument>.Update.PushEach(x => x.Dialogs, dialogElements);
|
var updateDialog = Builders<ConversationDialogDocument>.Update.PushEach(x => x.Dialogs, dialogElements)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
||||||
.Inc(x => x.DialogCount, dialogs.Count);
|
.Inc(x => x.DialogCount, dialogs.Count);
|
||||||
|
|
||||||
|
|
@ -190,7 +193,8 @@ public partial class MongoRepository
|
||||||
found.SecondaryRichContent = request.Message.RichContent;
|
found.SecondaryRichContent = request.Message.RichContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
var update = Builders<ConversationDialogDocument>.Update.Set(x => x.Dialogs, dialogs);
|
var update = Builders<ConversationDialogDocument>.Update.Set(x => x.Dialogs, dialogs)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
_dc.ConversationDialogs.UpdateOne(filter, update);
|
_dc.ConversationDialogs.UpdateOne(filter, update);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +212,8 @@ public partial class MongoRepository
|
||||||
Reason = breakpoint.Reason
|
Reason = breakpoint.Reason
|
||||||
};
|
};
|
||||||
var filterState = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
var filterState = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
var updateState = Builders<ConversationStateDocument>.Update.Push(x => x.Breakpoints, newBreakpoint);
|
var updateState = Builders<ConversationStateDocument>.Update.Push(x => x.Breakpoints, newBreakpoint)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
_dc.ConversationStates.UpdateOne(filterState, updateState);
|
_dc.ConversationStates.UpdateOne(filterState, updateState);
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +263,8 @@ public partial class MongoRepository
|
||||||
|
|
||||||
var filterStates = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
var filterStates = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
||||||
var saveStates = states.Select(x => StateMongoElement.ToMongoElement(x)).ToList();
|
var saveStates = states.Select(x => StateMongoElement.ToMongoElement(x)).ToList();
|
||||||
var updateStates = Builders<ConversationStateDocument>.Update.Set(x => x.States, saveStates);
|
var updateStates = Builders<ConversationStateDocument>.Update.Set(x => x.States, saveStates)
|
||||||
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||||
|
|
||||||
_dc.ConversationStates.UpdateOne(filterStates, updateStates);
|
_dc.ConversationStates.UpdateOne(filterStates, updateStates);
|
||||||
}
|
}
|
||||||
|
|
@ -500,7 +506,7 @@ public partial class MongoRepository
|
||||||
return conversationIds.Take(batchSize).ToList();
|
return conversationIds.Take(batchSize).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
|
public List<string> TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
|
||||||
{
|
{
|
||||||
var deletedMessageIds = new List<string>();
|
var deletedMessageIds = new List<string>();
|
||||||
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId))
|
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId))
|
||||||
|
|
@ -566,11 +572,13 @@ public partial class MongoRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
|
foundStates.UpdatedTime = DateTime.UtcNow;
|
||||||
_dc.ConversationStates.ReplaceOne(stateFilter, foundStates);
|
_dc.ConversationStates.ReplaceOne(stateFilter, foundStates);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save dialogs
|
// Save dialogs
|
||||||
foundDialog.Dialogs = truncatedDialogs;
|
foundDialog.Dialogs = truncatedDialogs;
|
||||||
|
foundDialog.UpdatedTime = DateTime.UtcNow;
|
||||||
_dc.ConversationDialogs.ReplaceOne(dialogFilter, foundDialog);
|
_dc.ConversationDialogs.ReplaceOne(dialogFilter, foundDialog);
|
||||||
|
|
||||||
// Update conversation
|
// Update conversation
|
||||||
|
|
@ -603,6 +611,27 @@ public partial class MongoRepository
|
||||||
return deletedMessageIds;
|
return deletedMessageIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<string> GetConversationSearchKeys(int messageLimit = 2, int convlimit = 100)
|
||||||
|
{
|
||||||
|
var convFilter = Builders<ConversationDocument>.Filter.Gte(x => x.DialogCount, messageLimit);
|
||||||
|
var conversations = _dc.Conversations.Find(convFilter)
|
||||||
|
.SortByDescending(x => x.UpdatedTime)
|
||||||
|
.Limit(convlimit)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (conversations.IsNullOrEmpty()) return [];
|
||||||
|
|
||||||
|
var convIds = conversations.Select(x => x.Id).ToList();
|
||||||
|
var stateFilter = Builders<ConversationStateDocument>.Filter.In(x => x.ConversationId, convIds);
|
||||||
|
|
||||||
|
var states = _dc.ConversationStates.Find(stateFilter).ToList();
|
||||||
|
var keys = states.SelectMany(x => x.States.Select(x => x.Key)).Distinct().ToList();
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private string ConvertSnakeCaseToPascalCase(string snakeCase)
|
private string ConvertSnakeCaseToPascalCase(string snakeCase)
|
||||||
{
|
{
|
||||||
string[] words = snakeCase.Split('_');
|
string[] words = snakeCase.Split('_');
|
||||||
|
|
|
||||||
|
|
@ -429,6 +429,11 @@ public partial class MongoRepository
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Dashboard? GetDashboard(string userId = null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void AddDashboardConversation(string userId, string conversationId)
|
public void AddDashboardConversation(string userId, string conversationId)
|
||||||
{
|
{
|
||||||
var user = _dc.Users.AsQueryable()
|
var user = _dc.Users.AsQueryable()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue