Merge branch 'master' of https://github.com/SciSharp/BotSharp into features/merge-origin-agent

This commit is contained in:
Jicheng Lu 2024-11-27 11:30:48 -06:00
commit 247afc4a88
49 changed files with 768 additions and 250 deletions

View file

@ -1,6 +1,7 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing;
namespace BotSharp.Abstraction.Routing.Planning;
namespace BotSharp.Abstraction.Planning;
public interface IExecutor
{

View file

@ -1,9 +1,19 @@
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Planning;
/// <summary>
/// Planning process for Task Agent
/// https://www.promptingguide.ai/techniques/cot
/// </summary>
public class ITaskPlanner
public interface ITaskPlanner
{
Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs);
List<RoleDialogModel> BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> dialogs;
bool AfterHandleContext(List<RoleDialogModel> dialogs, List<RoleDialogModel> taskAgentDialogs)
=> true;
int MaxLoopCount => 5;
}

View file

@ -32,9 +32,13 @@ public interface IBotSharpRepository : IHaveServiceProvider
List<User> GetUserByIds(List<string> ids) => throw new NotImplementedException();
List<User> GetUsersByAffiliateId(string affiliateId) => throw new NotImplementedException();
User? GetUserByUserName(string userName) => throw new NotImplementedException();
Dashboard? GetDashboard(string id = null) => throw new NotImplementedException();
void CreateUser(User user) => throw new NotImplementedException();
void UpdateExistUser(string userId, User user) => throw new NotImplementedException();
void UpdateUserVerified(string userId) => throw new NotImplementedException();
void AddDashboardConversation(string userId, string conversationId) => throw new NotImplementedException();
void RemoveDashboardConversation(string userId, string conversationId) => throw new NotImplementedException();
void UpdateDashboardConversation(string userId, DashboardConversation dashConv) => throw new NotImplementedException();
void UpdateUserVerificationCode(string userId, string verficationCode) => throw new NotImplementedException();
void UpdateUserPassword(string userId, string password) => throw new NotImplementedException();
void UpdateUserEmail(string userId, string email) => throw new NotImplementedException();

View file

@ -12,6 +12,11 @@ public class RuleType
/// </summary>
public const string DataValidation = "data-validation";
/// <summary>
/// The reasoning approach name for next step
/// </summary>
public const string Reasoner = "reasoner";
/// <summary>
/// The planning approach name for next step
/// </summary>

View file

@ -1,19 +0,0 @@
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Routing.Planning;
/// <summary>
/// Task breakdown and execution plan
/// https://www.promptingguide.ai/techniques/cot
/// </summary>
public interface IRoutingPlaner
{
Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs);
List<RoleDialogModel> BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> dialogs;
bool AfterHandleContext(List<RoleDialogModel> dialogs, List<RoleDialogModel> taskAgentDialogs)
=> true;
int MaxLoopCount => 5;
}

View file

@ -0,0 +1,30 @@
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Routing.Reasoning;
/// <summary>
/// Reasoning approaches for large language models (LLMs) help enhance their ability to solve complex problems,
/// handle tasks requiring logic, and provide accurate and contextually appropriate responses.
/// </summary>
public interface IRoutingReasoner
{
string Name => "Unnamed Reasoner";
string Description => "Each of these approaches leverages the capabilities of LLMs to reason more effectively, " +
"ensuring better performance and more coherent outputs across various types of complex tasks.";
int MaxLoopCount => 5;
Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> Task.FromResult(true);
Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> Task.FromResult(true);
List<RoleDialogModel> BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> dialogs;
bool AfterHandleContext(List<RoleDialogModel> dialogs, List<RoleDialogModel> taskAgentDialogs)
=> true;
}

View file

@ -29,4 +29,8 @@ public interface IUserService
Task<bool> UpdatePassword(string newPassword, string verificationCode);
Task<DateTime> GetUserTokenExpires();
Task<bool> UpdateUsersIsDisable(List<string> userIds, bool isDisable);
Task<bool> AddDashboardConversation(string userId, string conversationId);
Task<bool> RemoveDashboardConversation(string userId, string conversationId);
Task UpdateDashboardConversation(string userId, DashboardConversation dashConv);
Task<Dashboard?> GetDashboard(string userId);
}

View file

@ -0,0 +1,20 @@
namespace BotSharp.Abstraction.Users.Models;
public class Dashboard
{
public IList<DashboardConversation> ConversationList { get; set; } = [];
}
public class DashboardComponent
{
public required string Id { get; set; }
public string? Name { get; set; }
}
public class DashboardConversation : DashboardComponent
{
public string? ConversationId { get; set; }
public string? Instruction { get; set; } = "";
}

View file

@ -12,7 +12,6 @@ global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Infrastructures.Enums;
global using BotSharp.Abstraction.Models;
global using BotSharp.Abstraction.Routing.Models;
global using BotSharp.Abstraction.Routing.Planning;
global using BotSharp.Abstraction.Templating;
global using BotSharp.Abstraction.Translation.Attributes;
global using BotSharp.Abstraction.Messaging.Enums;

View file

@ -59,6 +59,11 @@
<ItemGroup>
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\database_knowledge.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.hf.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.naive.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.one-step-forward.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.sequential.get_remaining_task.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.sequential.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\agent.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\instructions\instruction.liquid" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\agent.json" />
@ -73,10 +78,6 @@
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\instructions\instruction.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\.welcome.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\conversation.summary.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.hf.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.naive.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.sequential.get_remaining_task.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.sequential.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\response_with_function.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\translation_prompt.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\select_file_prompt.liquid" />
@ -120,16 +121,19 @@
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\database_knowledge.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.sequential.get_remaining_task.liquid">
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.sequential.get_remaining_task.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.sequential.liquid">
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.sequential.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.hf.liquid">
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.hf.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.naive.liquid">
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.naive.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.one-step-forward.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\response_with_function.liquid">

View file

@ -1,13 +1,13 @@
using BotSharp.Abstraction.Google.Settings;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Instructs;
using BotSharp.Core.Messaging;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
using BotSharp.Core.Templating;
using BotSharp.Core.Translation;
using Microsoft.Extensions.Configuration;

View file

@ -41,6 +41,11 @@ public partial class FileRepository
return Users.FirstOrDefault(x => x.UserName == userName.ToLower());
}
public Dashboard? GetDashboard(string id = null)
{
return Dashboards.FirstOrDefault();
}
public void CreateUser(User user)
{
var userId = Guid.NewGuid().ToString();
@ -191,4 +196,68 @@ public partial class FileRepository
_users = [];
return true;
}
public void AddDashboardConversation(string userId, string conversationId)
{
var user = GetUserById(userId);
if (user == null) return;
// one user only has one dashboard currently
var dash = Dashboards.FirstOrDefault();
dash ??= new();
var existingConv = dash.ConversationList.FirstOrDefault(x => string.Equals(x.ConversationId, conversationId, StringComparison.OrdinalIgnoreCase));
if (existingConv != null) return;
var dashconv = new DashboardConversation
{
Id = Guid.NewGuid().ToString(),
ConversationId = conversationId
};
dash.ConversationList.Add(dashconv);
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId);
var path = Path.Combine(dir, DASHBOARD_FILE);
File.WriteAllText(path, JsonSerializer.Serialize(dash, _options));
}
public void RemoveDashboardConversation(string userId, string conversationId)
{
var user = GetUserById(userId);
if (user == null) return;
// one user only has one dashboard currently
var dash = Dashboards.FirstOrDefault();
if (dash == null) return;
var dashconv = dash.ConversationList.FirstOrDefault(
c => string.Equals(c.ConversationId, conversationId, StringComparison.OrdinalIgnoreCase));
if (dashconv == null) return;
dash.ConversationList.Remove(dashconv);
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId);
var path = Path.Combine(dir, DASHBOARD_FILE);
File.WriteAllText(path, JsonSerializer.Serialize(dash, _options));
}
public void UpdateDashboardConversation(string userId, DashboardConversation dashConv)
{
var user = GetUserById(userId);
if (user == null) return;
// one user only has one dashboard currently
var dash = Dashboards.FirstOrDefault();
if (dash == null) return;
var curIdx = dash.ConversationList.ToList().FindIndex(
x => string.Equals(x.ConversationId, dashConv.ConversationId, StringComparison.OrdinalIgnoreCase));
if (curIdx < 0) return;
dash.ConversationList[curIdx] = dashConv;
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId);
var path = Path.Combine(dir, DASHBOARD_FILE);
File.WriteAllText(path, JsonSerializer.Serialize(dash, _options));
}
}

View file

@ -22,6 +22,7 @@ public partial class FileRepository : IBotSharpRepository
private const string AGENT_FILE = "agent.json";
private const string AGENT_INSTRUCTION_FILE = "instruction";
private const string AGENT_SAMPLES_FILE = "samples.txt";
private const string DASHBOARD_FILE = "dashboard.json";
private const string AGENT_INSTRUCTIONS_FOLDER = "instructions";
private const string AGENT_FUNCTIONS_FOLDER = "functions";
private const string AGENT_TEMPLATES_FOLDER = "templates";
@ -83,6 +84,7 @@ public partial class FileRepository : IBotSharpRepository
private List<Role> _roles = new List<Role>();
private List<User> _users = new List<User>();
private List<Dashboard> _dashboards = [];
private List<Agent> _agents = new List<Agent>();
private List<RoleAgent> _roleAgents = new List<RoleAgent>();
private List<UserAgent> _userAgents = new List<UserAgent>();
@ -170,6 +172,36 @@ public partial class FileRepository : IBotSharpRepository
}
}
private IQueryable<Dashboard> Dashboards
{
get
{
if (!_dashboards.IsNullOrEmpty())
{
return _dashboards.AsQueryable();
}
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER);
_dashboards = [];
if (Directory.Exists(dir))
{
foreach (var d in Directory.GetDirectories(dir))
{
var dashboardFile = Path.Combine(d, DASHBOARD_FILE);
if (!Directory.Exists(d) || !File.Exists(dashboardFile))
continue;
var json = File.ReadAllText(dashboardFile);
var dash = JsonSerializer.Deserialize<Dashboard>(json, _options);
if (dash == null) continue;
_dashboards.Add(dash);
}
}
return _dashboards.AsQueryable();
}
}
private IQueryable<Agent> Agents
{
get

View file

@ -4,7 +4,7 @@ using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Core.Routing.Handlers;
@ -27,7 +27,7 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase//, IRoutingH
public List<string> Planers => new List<string>
{
nameof(HFPlanner)
nameof(HFReasoner)
};
public ContinueExecuteTaskRoutingHandler(IServiceProvider services, ILogger<ContinueExecuteTaskRoutingHandler> logger, RoutingSettings settings)

View file

@ -1,7 +1,7 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Core.Routing.Handlers;
@ -19,7 +19,7 @@ public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase//, IRouti
public List<string> Planers => new List<string>
{
nameof(HFPlanner)
nameof(HFReasoner)
};
public InterruptTaskExecutionRoutingHandler(IServiceProvider services, ILogger<InterruptTaskExecutionRoutingHandler> logger, RoutingSettings settings)

View file

@ -1,5 +1,5 @@
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Core.Routing.Handlers;
@ -26,7 +26,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase//, IRoutin
public List<string> Planers => new List<string>
{
nameof(HFPlanner)
nameof(HFReasoner)
};
public RetrieveDataFromAgentRoutingHandler(IServiceProvider services, ILogger<RetrieveDataFromAgentRoutingHandler> logger, RoutingSettings settings)

View file

@ -1,15 +0,0 @@
using System.Text.Json.Serialization;
public class FirstStagePlanParameter
{
[JsonPropertyName("input_args")]
public JsonDocument[] Parameters { get; set; } = new JsonDocument[0];
[JsonPropertyName("output_results")]
public string[] Results { get; set; } = new string[0];
public override string ToString()
{
return $"INPUTS:\r\n{JsonSerializer.Serialize(Parameters)}\r\n\r\nOUTPUTS:\r\n{JsonSerializer.Serialize(Results)}";
}
}

View file

@ -1,21 +0,0 @@
using System.Text.Json.Serialization;
namespace BotSharp.Core.Routing.Planning;
public class SecondStagePlan
{
[JsonPropertyName("related_tables")]
public string[] Tables { get; set; } = new string[0];
[JsonPropertyName("description")]
public string Description { get; set; } = "";
[JsonPropertyName("tool_name")]
public string Tool { get; set; } = "";
[JsonPropertyName("input_args")]
public JsonDocument[] Parameters { get; set; } = new JsonDocument[0];
[JsonPropertyName("output_results")]
public string[] Results { get; set; } = new string[0];
}

View file

@ -1,4 +0,0 @@
public class SecondStagePlanParameter : FirstStagePlanParameter
{
}

View file

@ -1,17 +1,33 @@
using BotSharp.Abstraction.Routing.Planning;
/*****************************************************************************
Copyright 2024 Written by Haiping Chen. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
/// <summary>
/// Human feedback based planner
/// Human feedback based reasoner
/// </summary>
public class HFPlanner : IRoutingPlaner
public class HFReasoner : IRoutingReasoner
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public HFPlanner(IServiceProvider services, ILogger<HFPlanner> logger)
public HFReasoner(IServiceProvider services, ILogger<HFReasoner> logger)
{
_services = services;
_logger = logger;
@ -37,7 +53,7 @@ public class HFPlanner : IRoutingPlaner
{
new RoleDialogModel(AgentRole.User, next)
{
FunctionName = nameof(HFPlanner),
FunctionName = nameof(HFReasoner),
MessageId = messageId
}
};
@ -60,7 +76,7 @@ public class HFPlanner : IRoutingPlaner
}
// Fix LLM malformed response
PlannerHelper.FixMalformedResponse(_services, inst);
ReasonerHelper.FixMalformedResponse(_services, inst);
return inst;
}
@ -87,13 +103,13 @@ public class HFPlanner : IRoutingPlaner
public async Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var context = _services.GetRequiredService<IRoutingContext>();
context.Empty(reason: $"Agent queue is cleared by {nameof(HFPlanner)}");
context.Empty(reason: $"Agent queue is cleared by {nameof(HFReasoner)}");
return true;
}
private string GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "planner_prompt.hf").Content;
var template = router.Templates.First(x => x.Name == "reasoner.hf").Content;
var render = _services.GetRequiredService<ITemplateRender>();
// update states
var conv = _services.GetRequiredService<IConversationService>();

View file

@ -1,6 +1,6 @@
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Planning;
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
public class InstructExecutor : IExecutor
{

View file

@ -1,16 +1,35 @@
/*****************************************************************************
Copyright 2024 Written by Haiping Chen. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
public class NaivePlanner : IRoutingPlaner
/// <summary>
/// simple or unsophisticated methods used to decide which specialized model or module in a system to engage for a given task.
/// </summary>
public class NaiveReasoner : IRoutingReasoner
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public NaivePlanner(IServiceProvider services, ILogger<NaivePlanner> logger)
public NaiveReasoner(IServiceProvider services, ILogger<NaiveReasoner> logger)
{
_services = services;
_logger = logger;
@ -46,7 +65,7 @@ public class NaivePlanner : IRoutingPlaner
{
new RoleDialogModel(AgentRole.User, next)
{
FunctionName = nameof(NaivePlanner),
FunctionName = nameof(NaiveReasoner),
MessageId = messageId
}
};
@ -69,7 +88,7 @@ public class NaivePlanner : IRoutingPlaner
}
// Fix LLM malformed response
PlannerHelper.FixMalformedResponse(_services, inst);
ReasonerHelper.FixMalformedResponse(_services, inst);
return inst;
}
@ -99,7 +118,7 @@ public class NaivePlanner : IRoutingPlaner
}
else
{
context.Empty(reason: $"Agent queue is cleared by {nameof(NaivePlanner)}");
context.Empty(reason: $"Agent queue is cleared by {nameof(NaiveReasoner)}");
// context.Push(inst.OriginalAgent, "Push user goal agent");
}
return true;
@ -107,7 +126,7 @@ public class NaivePlanner : IRoutingPlaner
private string GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "planner_prompt.naive").Content;
var template = router.Templates.First(x => x.Name == "reasoner.naive").Content;
var states = _services.GetRequiredService<IConversationStateService>();
var render = _services.GetRequiredService<ITemplateRender>();

View file

@ -0,0 +1,137 @@
/*****************************************************************************
Copyright 2024 Written by Haiping Chen. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Routing.Reasoning;
/// <summary>
/// One-step forward reasoning is a straightforward reasoning approach where the model or agent evaluates its current state
/// and takes the next best logical step toward the solution without extensive lookahead or planning.
/// This type of reasoning involves making a decision based on the current situation and immediate context
/// rather than considering multiple future steps or possibilities.
/// </summary>
public class OneStepForwardReasoner : IRoutingReasoner
{
public string Name => "one-step-forward";
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public OneStepForwardReasoner(IServiceProvider services, ILogger<NaiveReasoner> logger)
{
_services = services;
_logger = logger;
}
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs)
{
var next = GetNextStepPrompt(router);
var inst = new FunctionCallFromLlm();
// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: router?.LlmConfig?.Provider,
model: router?.LlmConfig?.Model);
int retryCount = 0;
while (retryCount < 3)
{
string text = string.Empty;
try
{
// text completion
// text = await completion.GetCompletion(content, router.Id, messageId);
dialogs = new List<RoleDialogModel>
{
new RoleDialogModel(AgentRole.User, next)
{
FunctionName = Name,
MessageId = messageId
}
};
var response = await completion.GetChatCompletions(router, dialogs);
inst = response.Content.JsonContent<FunctionCallFromLlm>();
break;
}
catch (Exception ex)
{
_logger.LogError($"{ex.Message}: {text}");
inst.Function = "response_to_user";
inst.Response = ex.Message;
inst.AgentName = "Router";
}
finally
{
retryCount++;
}
}
// Fix LLM malformed response
ReasonerHelper.FixMalformedResponse(_services, inst);
return inst;
}
public async Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
// Set user content as Planner's question
message.FunctionName = inst.Function;
message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments);
return true;
}
public async Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var context = _services.GetRequiredService<IRoutingContext>();
if (inst.UnmatchedAgent)
{
var unmatchedAgentId = context.GetCurrentAgentId();
// Exclude the wrong routed agent
var agents = router.TemplateDict["routing_agents"] as RoutableAgent[];
router.TemplateDict["routing_agents"] = agents.Where(x => x.AgentId != unmatchedAgentId).ToArray();
// Handover to Router;
context.Pop();
}
else
{
context.Empty(reason: $"Agent queue is cleared by {nameof(NaiveReasoner)}");
// context.Push(inst.OriginalAgent, "Push user goal agent");
}
return true;
}
private string GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "reasoner.one-step-forward").Content;
var states = _services.GetRequiredService<IConversationStateService>();
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
{
{ StateConst.EXPECTED_ACTION_AGENT, states.GetState(StateConst.EXPECTED_ACTION_AGENT) },
{ StateConst.EXPECTED_GOAL_AGENT, states.GetState(StateConst.EXPECTED_GOAL_AGENT) }
});
}
}

View file

@ -1,6 +1,6 @@
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
public static class PlannerHelper
public static class ReasonerHelper
{
/// <summary>
/// Sometimes LLM hallucinates and fails to set function names correctly.

View file

@ -1,11 +1,30 @@
/*****************************************************************************
Copyright 2024 Written by Haiping Chen. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
public class SequentialPlanner : IRoutingPlaner
/// <summary>
/// Sequential tasks focused reasoning approach
/// </summary>
public class SequentialReasoner : IRoutingReasoner
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
@ -14,7 +33,7 @@ public class SequentialPlanner : IRoutingPlaner
public int MaxLoopCount => 100;
private FunctionCallFromLlm _lastInst;
public SequentialPlanner(IServiceProvider services, ILogger<NaivePlanner> logger)
public SequentialReasoner(IServiceProvider services, ILogger<NaiveReasoner> logger)
{
_services = services;
_logger = logger;
@ -72,7 +91,7 @@ public class SequentialPlanner : IRoutingPlaner
{
new RoleDialogModel(AgentRole.User, next)
{
FunctionName = nameof(SequentialPlanner),
FunctionName = nameof(SequentialReasoner),
MessageId = messageId
}
};
@ -139,7 +158,7 @@ public class SequentialPlanner : IRoutingPlaner
if (message.StopCompletion)
{
context.Empty(reason: $"Agent queue is cleared by {nameof(SequentialPlanner)}");
context.Empty(reason: $"Agent queue is cleared by {nameof(SequentialReasoner)}");
return false;
}
@ -154,7 +173,7 @@ public class SequentialPlanner : IRoutingPlaner
private string GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "planner_prompt.sequential").Content;
var template = router.Templates.First(x => x.Name == "reasoner.sequential").Content;
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
@ -169,11 +188,11 @@ public class SequentialPlanner : IRoutingPlaner
var inst = new DecomposedStep();
var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
var model = llmProviderService.GetProviderModel("azure-openai", "gpt-4");
var model = llmProviderService.GetProviderModel("openai", "gpt-4o");
// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: "azure-openai",
provider: "openai",
model: model.Name);
int retryCount = 0;
@ -185,7 +204,7 @@ public class SequentialPlanner : IRoutingPlaner
var response = await completion.GetChatCompletions(new Agent
{
Id = router.Id,
Name = nameof(SequentialPlanner),
Name = nameof(SequentialReasoner),
Instruction = systemPrompt
}, dialogs);
@ -208,16 +227,11 @@ public class SequentialPlanner : IRoutingPlaner
private string GetDecomposeTaskPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "planner_prompt.sequential.get_remaining_task").Content;
var template = router.Templates.First(x => x.Name == "reasoner.sequential.get_remaining_task").Content;
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
{
});
}
public Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId)
{
throw new NotImplementedException();
}
}

View file

@ -1,10 +1,8 @@
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Settings;
using BotSharp.Core.Routing.Hooks;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
using Microsoft.Extensions.Configuration;
namespace BotSharp.Core.Routing;
@ -35,8 +33,10 @@ public class RoutingPlugin : IBotSharpPlugin
services.AddScoped<IRoutingService, RoutingService>();
services.AddScoped<IAgentHook, RoutingAgentHook>();
services.AddScoped<IRoutingPlaner, NaivePlanner>();
services.AddScoped<IRoutingPlaner, HFPlanner>();
services.AddScoped<IRoutingPlaner, SequentialPlanner>();
services.AddScoped<IRoutingReasoner, NaiveReasoner>();
services.AddScoped<IRoutingReasoner, HFReasoner>();
services.AddScoped<IRoutingReasoner, SequentialReasoner>();
services.AddScoped<IRoutingReasoner, OneStepForwardReasoner>();
}
}

View file

@ -1,24 +0,0 @@
using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing;
public partial class RoutingService
{
public IRoutingPlaner GetPlanner(Agent router)
{
var rule = router.RoutingRules.FirstOrDefault(x => x.Type == RuleType.Planner);
var planner = _services.GetServices<IRoutingPlaner>().
FirstOrDefault(x => x.GetType().Name.EndsWith(rule.Field));
if (planner == null)
{
_logger.LogError($"Can't find specific planner named {rule.Field}");
return _services.GetRequiredService<NaivePlanner>();
}
return planner;
}
}

View file

@ -0,0 +1,112 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Core.Routing;
public partial class RoutingService
{
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs)
{
RoleDialogModel response = default;
var agentService = _services.GetRequiredService<IAgentService>();
var convService = _services.GetRequiredService<IConversationService>();
var storage = _services.GetRequiredService<IConversationStorage>();
_router = await agentService.LoadAgent(message.CurrentAgentId);
var states = _services.GetRequiredService<IConversationStateService>();
var executor = _services.GetRequiredService<IExecutor>();
var planner = GetReasoner(_router);
_context.Push(_router.Id);
// Handle multi-language for input
var agentSettings = _services.GetRequiredService<AgentSettings>();
if (agentSettings.EnableTranslator)
{
var translator = _services.GetRequiredService<ITranslationService>();
var language = states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH);
if (language != LanguageType.ENGLISH)
{
message.SecondaryContent = message.Content;
message.Content = await translator.Translate(_router, message.MessageId, message.Content,
language: LanguageType.ENGLISH,
clone: false);
}
}
dialogs.Add(message);
storage.Append(convService.ConversationId, message);
// Get first instruction
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
int loopCount = 1;
while (true)
{
await HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnRoutingInstructionReceived(inst, message)
);
// Save states
states.SaveStateByArgs(inst.Arguments);
#if DEBUG
Console.WriteLine($"*** Next Instruction *** {inst}");
#else
_logger.LogInformation($"*** Next Instruction *** {inst}");
#endif
await planner.AgentExecuting(_router, inst, message, dialogs);
// Handover to Task Agent
if (inst.HandleDialogsByPlanner)
{
var dialogWithoutContext = planner.BeforeHandleContext(inst, message, dialogs);
response = await executor.Execute(this, inst, message, dialogWithoutContext);
planner.AfterHandleContext(dialogs, dialogWithoutContext);
}
else
{
response = await executor.Execute(this, inst, message, dialogs);
}
await planner.AgentExecuted(_router, inst, response, dialogs);
if (loopCount >= planner.MaxLoopCount || _context.IsEmpty)
{
break;
}
// Get next instruction from Planner
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
loopCount++;
}
return response;
}
public IRoutingReasoner GetReasoner(Agent router)
{
var rule = router.RoutingRules.FirstOrDefault(x => x.Type == RuleType.Reasoner);
var reasoner = _services.GetServices<IRoutingReasoner>().
FirstOrDefault(x => x.GetType().Name.EndsWith(rule.Field));
if (reasoner == null)
{
_logger.LogError($"Can't find specific planner named {rule.Field}");
// Default use NaiveReasoner
return _services.GetRequiredService<NaiveReasoner>();
}
return reasoner;
}
}

View file

@ -1,6 +1,4 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Core.Routing;
@ -60,97 +58,12 @@ public partial class RoutingService : IRoutingService
return response;
}
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs)
{
RoleDialogModel response = default;
var agentService = _services.GetRequiredService<IAgentService>();
var convService = _services.GetRequiredService<IConversationService>();
var storage = _services.GetRequiredService<IConversationStorage>();
_router = await agentService.LoadAgent(message.CurrentAgentId);
var states = _services.GetRequiredService<IConversationStateService>();
var executor = _services.GetRequiredService<IExecutor>();
var planner = GetPlanner(_router);
_context.Push(_router.Id);
// Handle multi-language for input
var agentSettings = _services.GetRequiredService<AgentSettings>();
if (agentSettings.EnableTranslator)
{
var translator = _services.GetRequiredService<ITranslationService>();
var language = states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH);
if (language != LanguageType.ENGLISH)
{
message.SecondaryContent = message.Content;
message.Content = await translator.Translate(_router, message.MessageId, message.Content,
language: LanguageType.ENGLISH,
clone: false);
}
}
dialogs.Add(message);
storage.Append(convService.ConversationId, message);
// Get first instruction
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
int loopCount = 1;
while (true)
{
await HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnRoutingInstructionReceived(inst, message)
);
// Save states
states.SaveStateByArgs(inst.Arguments);
#if DEBUG
Console.WriteLine($"*** Next Instruction *** {inst}");
#else
_logger.LogInformation($"*** Next Instruction *** {inst}");
#endif
await planner.AgentExecuting(_router, inst, message, dialogs);
// Handover to Task Agent
if (inst.HandleDialogsByPlanner)
{
var dialogWithoutContext = planner.BeforeHandleContext(inst, message, dialogs);
response = await executor.Execute(this, inst, message, dialogWithoutContext);
planner.AfterHandleContext(dialogs, dialogWithoutContext);
}
else
{
response = await executor.Execute(this, inst, message, dialogs);
}
await planner.AgentExecuted(_router, inst, response, dialogs);
if (loopCount >= planner.MaxLoopCount || _context.IsEmpty)
{
break;
}
// Get next instruction from Planner
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
loopCount++;
}
return response;
}
public List<RoutingHandlerDef> GetHandlers(Agent router)
{
var planer = GetPlanner(router);
var reasoner = GetReasoner(router);
return _services.GetServices<IRoutingHandler>()
.Where(x => x.Planers == null || x.Planers.Contains(planer.GetType().Name))
.Where(x => x.Planers == null || x.Planers.Contains(reasoner.GetType().Name))
.Where(x => !string.IsNullOrEmpty(x.Description))
.Select((x, i) => new RoutingHandlerDef
{

View file

@ -736,4 +736,42 @@ public class UserService : IUserService
}
return true;
}
public async Task<bool> AddDashboardConversation(string userId, string conversationId)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
db.AddDashboardConversation(userId, conversationId);
await Task.CompletedTask;
return true;
}
public async Task<bool> RemoveDashboardConversation(string userId, string conversationId)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
db.RemoveDashboardConversation(userId, conversationId);
await Task.CompletedTask;
return true;
}
public async Task UpdateDashboardConversation(string userId, DashboardConversation newDashConv)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var dashConv = db.GetDashboard(userId)?.ConversationList.FirstOrDefault(x => string.Equals(x.ConversationId, newDashConv.ConversationId));
if (dashConv == null) return;
dashConv.Name = newDashConv.Name ?? dashConv.Name;
dashConv.Instruction = newDashConv.Instruction ?? dashConv.Instruction;
db.UpdateDashboardConversation(userId, dashConv);
await Task.CompletedTask;
return;
}
public async Task<Dashboard?> GetDashboard(string userId)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var dash = db.GetDashboard();
await Task.CompletedTask;
return dash;
}
}

View file

@ -11,8 +11,8 @@
"profiles": [ "tool" ],
"routingRules": [
{
"type": "planner",
"field": "HFPlanner"
"type": "reasoner",
"field": "HFReasoner"
}
]
}

View file

@ -0,0 +1,2 @@
Analyze the user's problem. Which prerequisite task needs to be completed? Output the next step of routing instructions.
Check the job responsibilities of the routable Agent and do not transfer to an Agent that exceeds the scope of responsibility.

View file

@ -511,6 +511,28 @@ public class ConversationController : ControllerBase
}
#endregion
#region miscellaneous
[HttpPut("/agent/{agentId}/conversation/{conversationId}/dashboard")]
public async Task<bool> PinConversationToDashboard([FromRoute] string agentId, [FromRoute] string conversationId)
{
var userService = _services.GetRequiredService<IUserService>();
var user = await userService.GetUser(_user.Id);
var pinned = await userService.AddDashboardConversation(user.Id, conversationId);
return pinned;
}
[HttpDelete("/agent/{agentId}/conversation/{conversationId}/dashboard")]
public async Task<bool> UnpinConversationFromDashboard([FromRoute] string agentId, [FromRoute] string conversationId)
{
var userService = _services.GetRequiredService<IUserService>();
var user = await userService.GetUser(_user.Id);
var unpinned = await userService.RemoveDashboardConversation(user.Id, conversationId);
return unpinned;
}
#endregion
#region Private methods
private void SetStates(IConversationService conv, NewMessageModel input)
{

View file

@ -0,0 +1,73 @@
using BotSharp.Abstraction.Options;
using BotSharp.Abstraction.Users.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BotSharp.OpenAPI.Controllers;
[Authorize]
[ApiController]
public class DashboardController : ControllerBase
{
private readonly IServiceProvider _services;
private readonly IUserIdentity _user;
public DashboardController(IServiceProvider services,
IUserIdentity user,
BotSharpOptions options)
{
_services = services;
_user = user;
}
#region User Components
[HttpGet("/dashboard/components")]
public async Task<UserDashboardModel> GetComponents(string userId)
{
var userService = _services.GetRequiredService<IUserService>();
var dashboardProfile = await userService.GetDashboard(userId);
if (dashboardProfile == null) return new UserDashboardModel();
var result = new UserDashboardModel
{
ConversationList = dashboardProfile.ConversationList.Select(
x => new UserDashboardConversationModel
{
Name = x.Name,
ConversationId = x.ConversationId,
Instruction = x.Instruction
}
).ToList()
};
return result;
}
[HttpPost("/dashboard/component/conversation")]
public async Task UpdateDashboardConversationInstruction(string userId, UserDashboardConversationModel dashConv)
{
if (string.IsNullOrEmpty(dashConv.Name) && string.IsNullOrEmpty(dashConv.Instruction))
{
return;
}
var newDashConv = new DashboardConversation
{
Id = Guid.Empty.ToString(),
ConversationId = dashConv.ConversationId
};
if (!string.IsNullOrEmpty(dashConv.Name))
{
newDashConv.Name = dashConv.Name;
}
if (!string.IsNullOrEmpty(dashConv.Instruction))
{
newDashConv.Instruction = dashConv.Instruction;
}
var userService = _services.GetRequiredService<IUserService>();
await userService.UpdateDashboardConversation(userId, newDashConv);
return;
}
#endregion
}

View file

@ -12,6 +12,7 @@ public class ConversationViewModel
[JsonPropertyName("agent_name")]
public string AgentName { get; set; }
[JsonPropertyName("title")]
public string Title { get; set; } = string.Empty;
public UserViewModel User { get; set; } = new UserViewModel();

View file

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace BotSharp.OpenAPI.ViewModels.Users;
public class UserDashboardModel
{
[JsonPropertyName("conversation_list")]
public IList<UserDashboardConversationModel> ConversationList { get; set; } = [];
}
public class UserDashboardConversationModel
{
[JsonPropertyName("name")]
public string? Name { get; set; }
[JsonPropertyName("conversation_id")]
public string? ConversationId { get; set; }
[JsonPropertyName("instruction")]
public string? Instruction { get; set; }
}

View file

@ -8,7 +8,7 @@
"updatedDateTime": "2024-11-23T00:00:00Z",
"disabled": false,
"isPublic": true,
"profiles": [ "database" ],
"profiles": [ "coding" ],
"llmConfig": {
"provider": "openai",
"model": "gpt-4o",

View file

@ -12,7 +12,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.0.0-preview.9.24556.5" />
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.0.1-preview.1.24570.5" />
<PackageReference Include="System.Text.Encodings.Web" Version="8.0.0" />
</ItemGroup>

View file

@ -26,6 +26,8 @@ public class UserDocument : MongoBase
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
public Dashboard? Dashboard { get; set; }
public User ToUser()
{
return new User

View file

@ -317,4 +317,51 @@ public partial class MongoRepository
return true;
}
public void AddDashboardConversation(string userId, string conversationId)
{
var user = _dc.Users.AsQueryable()
.FirstOrDefault(x => x.Id == userId || (x.ExternalId != null && x.ExternalId == userId));
if (user == null) return;
var curDash = user.Dashboard ?? new Dashboard();
curDash.ConversationList.Add(new DashboardConversation
{
Id = Guid.NewGuid().ToString(),
ConversationId = conversationId
});
var filter = Builders<UserDocument>.Filter.Eq(x => x.Id, userId);
var update = Builders<UserDocument>.Update.Set(x => x.Dashboard, curDash)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
}
public void RemoveDashboardConversation(string userId, string conversationId)
{
var user = _dc.Users.AsQueryable()
.FirstOrDefault(x => x.Id == userId || (x.ExternalId != null && x.ExternalId == userId));
if (user == null || user.Dashboard == null || user.Dashboard.ConversationList.IsNullOrEmpty()) return;
var curDash = user.Dashboard;
var unpinConv = user.Dashboard.ConversationList.FirstOrDefault(
x => string.Equals(x.ConversationId, conversationId, StringComparison.OrdinalIgnoreCase));
if (unpinConv == null) return;
curDash.ConversationList.Remove(unpinConv);
var filter = Builders<UserDocument>.Filter.Eq(x => x.Id, userId);
var update = Builders<UserDocument>.Update.Set(x => x.Dashboard, curDash)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
}
public void UpdateDashboardConversation(string userId, DashboardConversation dashConv)
{
var user = _dc.Users.AsQueryable()
.FirstOrDefault(x => x.Id == userId || (x.ExternalId != null && x.ExternalId == userId));
if (user == null || user.Dashboard == null || user.Dashboard.ConversationList.IsNullOrEmpty()) return;
var curIdx = user.Dashboard.ConversationList.ToList().FindIndex(
x => string.Equals(x.ConversationId, dashConv.ConversationId, StringComparison.OrdinalIgnoreCase));
if (curIdx < 0) return;
var filter = Builders<UserDocument>.Filter.Eq(x => x.Id, userId);
var update = Builders<UserDocument>.Update.Set(x => x.Dashboard.ConversationList[curIdx], dashConv)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
}
}

View file

@ -1,4 +1,4 @@
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Planning;
using BotSharp.Plugin.Planner.TwoStaging;
namespace BotSharp.Plugin.Planner;
@ -17,7 +17,7 @@ public class PlannerPlugin : IBotSharpPlugin
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped<IRoutingPlaner, TwoStageTaskPlanner>();
services.AddScoped<ITaskPlanner, TwoStageTaskPlanner>();
services.AddScoped<IAgentHook, PlannerAgentHook>();
services.AddScoped<IAgentUtilityHook, PlannerUtilityHook>();
}

View file

@ -1,10 +1,10 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Core.Routing.Planning;
using BotSharp.Abstraction.Planning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Plugin.Planner.TwoStaging;
public partial class TwoStageTaskPlanner : IRoutingPlaner
public partial class TwoStageTaskPlanner : ITaskPlanner
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
@ -40,7 +40,7 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
inst = response.Content.JsonContent<FunctionCallFromLlm>();
// Fix LLM malformed response
PlannerHelper.FixMalformedResponse(_services, inst);
ReasonerHelper.FixMalformedResponse(_services, inst);
return inst;
}

View file

@ -1,4 +1,5 @@
You are a planning summarizer. You will generate the final output in JSON format with short explanation based on the task description, knowledge and related table structure and relationship.
You are a planning summarizer. You will generate the final output in JSON format based on the task description, knowledge and related table structure and relationship.
Generate a simple business explaination of the quried data for the non tech audience.
Requirements:
{{ summary_requirements }}

View file

@ -62,7 +62,7 @@ public class SqlValidateFn : IFunctionCallback
{
Provider = agent?.LlmConfig?.Provider ?? "openai",
Model = agent?.LlmConfig?.Model ?? "gpt-4o",
Message = "Correct SQL Statement",
Message = "Correct SQL Statement and keep the comments/explanations",
Data = new Dictionary<string, object>
{
{ "original_sql", message.Content },

View file

@ -1,7 +1,7 @@
{
"id": "beda4c12-e1ec-4b4b-b328-3df4a6687c4f",
"name": "SQL Driver",
"description": "Execute the sql query in database from the latest dialog.",
"description": "Transfer to this Agent is allowed only when executable SQL statements are provided in the context.",
"iconUrl": "https://cdn-icons-png.flaticon.com/512/3161/3161158.png",
"type": "task",
"createdDateTime": "2023-11-15T13:49:00Z",
@ -18,7 +18,7 @@
"field": "sql_statement",
"required": true,
"field_type": "string",
"description": "SQL statement"
"description": "SQL statement provided in the context"
}
]
}