Merge branch 'SciSharp:master' into master

This commit is contained in:
hchen2020 2023-10-27 10:36:46 -05:00 committed by GitHub
commit 0cbb0183cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 103 additions and 64 deletions

View file

@ -1,6 +1,6 @@
# Messaging Components
Conversations are a lot more than simple text messages when you are building a AI chatbot. In addition to text, the `BotSharp`` allows you to send rich-media, like audio, video, and images, and provides a set of structured messaging options in the form of message templates, quick replies, buttons and more. The UI rendering program can render components according to the returned data format.
Conversations are a lot more than simple text messages when you are building a AI chatbot. In addition to text, the `BotSharp` allows you to send rich-media, like audio, video, and images, and provides a set of structured messaging options in the form of message templates, quick replies, buttons and more. The UI rendering program can render components according to the returned data format.
## Text Messages
@ -75,7 +75,7 @@ Message templates are structured message formats used for various purposes to pr
...
}
]
}
}
}
}
}

View file

@ -1,36 +1,9 @@
using BotSharp.Abstraction.Models;
namespace BotSharp.Abstraction.Conversations.Models;
public class IncomingMessageModel
public class IncomingMessageModel : MessageConfig
{
public string Text { get; set; } = string.Empty;
public virtual string Channel { get; set; } = string.Empty;
/// <summary>
/// Completion Provider
/// </summary>
[JsonPropertyName("provider")]
public virtual string? Provider { get; set; } = null;
/// <summary>
/// Model name
/// </summary>
[JsonPropertyName("model")]
public virtual string? Model { get; set; } = null;
/// <summary>
/// The sampling temperature to use that controls the apparent creativity of generated completions.
/// </summary>
public float Temperature { get; set; } = 0.5f;
/// <summary>
/// An alternative value to Temperature, called nucleus sampling, that causes
/// the model to consider the results of the tokens with probability mass.
/// </summary>
public float SamplingFactor { get; set; } = 0.5f;
/// <summary>
/// Conversation states from input
/// </summary>
public List<string> States { get; set; } = new List<string>();
public virtual string Channel { get; set; }
}

View file

@ -1,7 +1,11 @@
using BotSharp.Abstraction.Models;
namespace BotSharp.Abstraction.Conversations.Models;
public class RoleDialogModel
public class RoleDialogModel : ITrackableMessage
{
public string MessageId { get; set; }
/// <summary>
/// user, system, assistant, function
/// </summary>
@ -34,10 +38,15 @@ public class RoleDialogModel
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
public bool StopCompletion { get; set; }
private RoleDialogModel()
{
}
public RoleDialogModel(string role, string text)
{
Role = role;
Content = text;
MessageId = Guid.NewGuid().ToString();
}
public override string ToString()

View file

@ -1,7 +1,10 @@
using BotSharp.Abstraction.Models;
namespace BotSharp.Abstraction.Instructs.Models;
public class InstructResult
public class InstructResult : ITrackableMessage
{
public string MessageId { get; set; }
public string Text { get; set; }
public object Data { get; set; }
public ConversationState States { get; set; }

View file

@ -0,0 +1,9 @@
namespace BotSharp.Abstraction.Models;
/// <summary>
/// Define a message ID to extend message-level applications, such as model fees, token usage, and data collection
/// </summary>
public interface ITrackableMessage
{
string MessageId { get; set; }
}

View file

@ -0,0 +1,32 @@
namespace BotSharp.Abstraction.Models;
public class MessageConfig
{
/// <summary>
/// Completion Provider
/// </summary>
[JsonPropertyName("provider")]
public virtual string? Provider { get; set; } = null;
/// <summary>
/// Model name
/// </summary>
[JsonPropertyName("model")]
public virtual string? Model { get; set; } = null;
/// <summary>
/// The sampling temperature to use that controls the apparent creativity of generated completions.
/// </summary>
public float Temperature { get; set; } = 0.5f;
/// <summary>
/// An alternative value to Temperature, called nucleus sampling, that causes
/// the model to consider the results of the tokens with probability mass.
/// </summary>
public float SamplingFactor { get; set; } = 0.5f;
/// <summary>
/// Conversation states from input
/// </summary>
public List<string> States { get; set; } = new List<string>();
}

View file

@ -1,7 +1,10 @@
namespace BotSharp.Abstraction.Routing.Models;
public class RoutingArgs
public class RoutingArgs : ITrackableMessage
{
[JsonPropertyName("message_id")]
public string MessageId { get; set; }
[JsonPropertyName("function")]
public string Function { get; set; }

View file

@ -7,4 +7,5 @@ global using System.ComponentModel.DataAnnotations;
global using System.Text.Json.Serialization;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Models;

View file

@ -78,11 +78,11 @@ public partial class ConversationService : IConversationService
throw new NotImplementedException();
}
public List<RoleDialogModel> GetDialogHistory(int lastCount = 20)
public List<RoleDialogModel> GetDialogHistory(int lastCount = 50)
{
var dialogs = _storage.GetDialogs(_conversationId);
return dialogs
.Where(x => x.CreatedAt > DateTime.UtcNow.AddHours(-8))
.Where(x => x.CreatedAt > DateTime.UtcNow.AddHours(-24))
.TakeLast(lastCount)
.ToList();
}

View file

@ -6,19 +6,13 @@ namespace BotSharp.Core.Conversations.Services;
public class ConversationStorage : IConversationStorage
{
private readonly BotSharpDatabaseSettings _dbSettings;
private readonly AgentSettings _agentSettings;
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
public ConversationStorage(
BotSharpDatabaseSettings dbSettings,
AgentSettings agentSettings,
IServiceProvider services,
IUserIdentity user)
IServiceProvider services)
{
_dbSettings = dbSettings;
_agentSettings = agentSettings;
_services = services;
_user = user;
}
public void Append(string conversationId, RoleDialogModel dialog)
@ -32,7 +26,7 @@ public class ConversationStorage : IConversationStorage
{
var args = dialog.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim();
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{dialog.FunctionName}|{args}");
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{dialog.MessageId}");
var content = dialog.Content;
content = content.Replace("\r", " ").Replace("\n", " ").Trim();
@ -44,9 +38,7 @@ public class ConversationStorage : IConversationStorage
}
else
{
var agentName = db.GetAgent(agentId)?.Name;
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{agentName}|");
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{dialog.MessageId}");
var content = dialog.Content.Replace("\r", " ").Replace("\n", " ").Trim();
if (string.IsNullOrEmpty(content))
{
@ -73,15 +65,13 @@ public class ConversationStorage : IConversationStorage
var createdAt = DateTime.Parse(meta.Split('|')[0]);
var role = meta.Split('|')[1];
var currentAgentId = meta.Split('|')[2];
var funcName = meta.Split('|')[3];
var funcArgs= meta.Split('|')[4];
var messageId = meta.Split('|')[3];
var text = dialog.Substring(4);
results.Add(new RoleDialogModel(role, text)
{
CurrentAgentId = currentAgentId,
FunctionName = funcName,
FunctionArgs = funcArgs,
MessageId = messageId,
Content = text,
CreatedAt = createdAt
});

View file

@ -33,6 +33,7 @@ public partial class InstructService : IInstructService
{
return new InstructResult
{
MessageId = message.MessageId,
Text = message.Content
};
}
@ -42,6 +43,7 @@ public partial class InstructService : IInstructService
var result = await completer.GetCompletion(agent.Instruction);
var response = new InstructResult
{
MessageId = message.MessageId,
Text = result
};

View file

@ -33,6 +33,7 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase, IRoutingHan
var result = new RoleDialogModel(AgentRole.Function, inst.Question)
{
MessageId = inst.MessageId,
FunctionName = inst.Function,
FunctionArgs = JsonSerializer.Serialize(inst.Arguments),
CurrentAgentId = record.Id

View file

@ -28,6 +28,7 @@ public class ConversationEndRoutingHandler : RoutingHandlerBase, IRoutingHandler
{
var result = new RoleDialogModel(AgentRole.Assistant, inst.Response)
{
MessageId = inst.MessageId,
CurrentAgentId = _settings.RouterId,
FunctionName = inst.Function,
Data = inst

View file

@ -29,6 +29,7 @@ public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandle
{
var result = new RoleDialogModel(AgentRole.Assistant, inst.Response)
{
MessageId = inst.MessageId,
CurrentAgentId = _settings.RouterId,
FunctionName = inst.Function,
Data = inst

View file

@ -28,6 +28,7 @@ public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase, IRouting
{
var result = new RoleDialogModel(AgentRole.User, inst.Reason)
{
MessageId = inst.MessageId,
FunctionName = inst.Function,
StopCompletion = true
};

View file

@ -28,6 +28,7 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler
{
var result = new RoleDialogModel(AgentRole.Assistant, inst.Response)
{
MessageId = inst.MessageId,
CurrentAgentId = _settings.RouterId,
FunctionName = inst.Function,
Data = inst,

View file

@ -45,6 +45,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH
/*_dialogs.Add(new RoleDialogModel(AgentRole.Function, inst.Parameters.Answer)
{
MessageId = inst.MessageId,
FunctionName = inst.Function,
FunctionArgs = JsonSerializer.Serialize(inst.Parameters.Arguments),
ExecutionResult = inst.Parameters.Answer,

View file

@ -35,6 +35,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
var function = _services.GetServices<IFunctionCallback>().FirstOrDefault(x => x.Name == inst.Function);
var message = new RoleDialogModel(AgentRole.Function, inst.Question)
{
MessageId = inst.MessageId,
FunctionName = inst.Function,
FunctionArgs = JsonSerializer.Serialize(inst),
CurrentAgentId = context.GetCurrentAgentId(),
@ -46,6 +47,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
// Keep last message data for debug
result.Data = result.Data ?? message.Data;
result.FunctionName = result.FunctionName ?? message.FunctionName;
return result;
}
}

View file

@ -27,6 +27,7 @@ public class TaskEndRoutingHandler : RoutingHandlerBase, IRoutingHandler
{
var result = new RoleDialogModel(AgentRole.Assistant, inst.Response)
{
MessageId = inst.MessageId,
CurrentAgentId = _settings.RouterId,
FunctionName = inst.Function,
Data = inst

View file

@ -76,7 +76,7 @@ public partial class RoutingService : IRoutingService
CurrentAgentId = router.Id
};
var message = Dialogs.Last().Content;
var inputMsg = Dialogs.Last();
var handlers = _services.GetServices<IRoutingHandler>();
@ -87,7 +87,8 @@ public partial class RoutingService : IRoutingService
loopCount++;
var inst = await GetNextInstruction();
inst.Question = inst.Question ?? message;
inst.MessageId = inputMsg.MessageId;
inst.Question = inst.Question ?? inputMsg.Content;
var handler = handlers.FirstOrDefault(x => x.Name == inst.Function);
if (handler == null)
@ -99,6 +100,7 @@ public partial class RoutingService : IRoutingService
handler.SetDialogs(Dialogs);
result = await handler.Handle(this, inst);
result.MessageId = inputMsg.MessageId;
stop = !_settings.EnableReasoning;
}

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.ApiAdapters;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Models;
using BotSharp.OpenAPI.ViewModels.Conversations;
namespace BotSharp.OpenAPI.Controllers;
@ -19,15 +20,17 @@ public class ConversationController : ControllerBase, IApiAdapter
}
[HttpPost("/conversation/{agentId}")]
public async Task<ConversationViewModel> NewConversation([FromRoute] string agentId)
public async Task<ConversationViewModel> NewConversation([FromRoute] string agentId, [FromBody] MessageConfig config)
{
var service = _services.GetRequiredService<IConversationService>();
var sess = new Conversation
var conv = new Conversation
{
AgentId = agentId
};
sess = await service.NewConversation(sess);
return ConversationViewModel.FromSession(sess);
conv = await service.NewConversation(conv);
config.States.ForEach(x => conv.States[x.Split('=')[0]] = x.Split('=')[1]);
return ConversationViewModel.FromSession(conv);
}
[HttpDelete("/conversation/{agentId}/{conversationId}")]
@ -51,9 +54,8 @@ public class ConversationController : ControllerBase, IApiAdapter
var response = new MessageResponseModel();
var stackMsg = new List<RoleDialogModel>();
await conv.SendMessage(agentId,
new RoleDialogModel("user", input.Text),
var inputMsg = new RoleDialogModel("user", input.Text);
await conv.SendMessage(agentId, inputMsg,
async msg =>
{
stackMsg.Add(msg);
@ -71,6 +73,7 @@ public class ConversationController : ControllerBase, IApiAdapter
response.Text = string.Join("\r\n", stackMsg.Select(x => x.Content));
response.Data = response.Data ?? stackMsg.Last().Data;
response.Function = stackMsg.Last().FunctionName;
response.MessageId = inputMsg.MessageId;
return response;
}

View file

@ -1,7 +1,10 @@
using BotSharp.Abstraction.Models;
namespace BotSharp.OpenAPI.ViewModels.Conversations;
public class MessageResponseModel
public class MessageResponseModel : ITrackableMessage
{
public string MessageId { get; set; }
public string Text { get; set; }
public string Function { get; set; }
public object Data { get; set; }