Merge branch 'SciSharp:master' into master

This commit is contained in:
Haiping 2024-12-07 14:26:31 +00:00 committed by GitHub
commit c387ea0d82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 275 additions and 55 deletions

View file

@ -56,9 +56,4 @@ public class BuiltInAgentId
/// Translates user-defined natural language rules into programmatic code
/// </summary>
public const string RuleEncoder = "6acfb93c-3412-402e-9ba5-c5d3cd8f0161";
/// <summary>
/// Schedule job
/// </summary>
public const string Crontab = "c2o139da-a62a-4355-8605-fdf0ffaca58e";
}

View file

@ -2,9 +2,11 @@ using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Messaging.Models.RichContent;
using System.Diagnostics;
namespace BotSharp.Abstraction.Conversations.Models;
[DebuggerStepThrough]
public class RoleDialogModel : ITrackableMessage
{
/// <summary>

View file

@ -7,11 +7,15 @@
</PropertyGroup>
<ItemGroup>
<None Remove="data\agents\c2o139da-a62a-4355-8605-fdf0ffaca58e\agent.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-crontab-schedule_task.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-crontab-schedule_task.fn.liquid" />
</ItemGroup>
<ItemGroup>
<Content Include="data\agents\c2o139da-a62a-4355-8605-fdf0ffaca58e\agent.json">
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\util-crontab-schedule_task.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\util-crontab-schedule_task.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

View file

@ -14,6 +14,8 @@
limitations under the License.
******************************************************************************/
using BotSharp.Core.Crontab.Hooks;
namespace BotSharp.Core.Crontab;
/// <summary>
@ -27,13 +29,9 @@ public class CrontabPlugin : IBotSharpPlugin
public string Description => "Crontab plugin is a time-based job scheduler in agent framework. The cron system is used to trigger AI Agent to do specific task periodically.";
public string IconUrl => "https://icon-library.com/images/stop-watch-icon/stop-watch-icon-10.jpg";
public string[] AgentIds =
[
BuiltInAgentId.Crontab
];
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped<IAgentUtilityHook, CrontabUtilityHook>();
services.AddScoped<ICrontabService, CrontabService>();
services.AddHostedService<CrontabWatcher>();
}

View file

@ -0,0 +1,6 @@
namespace BotSharp.Core.Crontab.Enum;
public class UtilityName
{
public const string ScheduleTask = "crontab.schedule-task";
}

View file

@ -0,0 +1,37 @@
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Users;
using BotSharp.Core.Crontab.Hooks;
namespace BotSharp.Core.Crontab.Functions;
public class ScheduleTaskFn : IFunctionCallback
{
public string Name => $"{CrontabUtilityHook.PREFIX}schedule_task";
private readonly IServiceProvider _services;
public ScheduleTaskFn(IServiceProvider services)
{
_services = services;
}
public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<ScheduleTaskArgs>(message.FunctionArgs);
var routing = _services.GetRequiredService<IRoutingContext>();
var user = _services.GetRequiredService<IUserIdentity>();
var crontabItem = new CrontabItem
{
Topic = args.Topic,
Description = args.Description,
Cron = args.Cron,
Script = args.Script,
Language = args.Language,
UserId = user.Id,
AgentId = routing.EntryAgentId,
ConversationId = routing.ConversationId
};
return true;
}
}

View file

@ -0,0 +1,25 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Core.Crontab.Enum;
namespace BotSharp.Core.Crontab.Hooks;
public class CrontabUtilityHook : IAgentUtilityHook
{
public const string PREFIX = "util-crontab-";
private const string SCHEDULE_TASK_FN = $"{PREFIX}schedule_task";
public void AddUtilities(List<AgentUtility> utilities)
{
var items = new List<AgentUtility>
{
new AgentUtility
{
Name = UtilityName.ScheduleTask,
Functions = [new(SCHEDULE_TASK_FN)],
Templates = [new($"{SCHEDULE_TASK_FN}.fn")]
}
};
utilities.AddRange(items);
}
}

View file

@ -1,14 +1,14 @@
namespace BotSharp.Core.Crontab.Models;
public class CrontabItem
public class CrontabItem : ScheduleTaskArgs
{
public string UserId { get; set; } = null!;
public string AgentId { get; set; } = null!;
public string Topic { get; set; } = null!;
public string Cron { get; set; } = null!;
public string ConversationId { get; set; } = null!;
public string ExecutionResult { get; set; } = null!;
public override string ToString()
{
return $"AgentId: {AgentId}, UserId: {UserId}, Topic: {Topic}";
return $"{Topic}: {Description} [AgentId: {AgentId}, UserId: {UserId}]";
}
}

View file

@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace BotSharp.Core.Crontab.Models;
public class ScheduleTaskArgs
{
[JsonPropertyName("cron_expression")]
public string Cron { get; set; } = null!;
[JsonPropertyName("topic")]
public string Topic { get; set; } = null!;
[JsonPropertyName("description")]
public string Description { get; set; } = null!;
[JsonPropertyName("script")]
public string Script { get; set; } = null!;
[JsonPropertyName("language")]
public string Language { get; set; } = null!;
}

View file

@ -15,6 +15,8 @@
******************************************************************************/
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Crontab.Models;
using BotSharp.Core.Infrastructures;
@ -56,22 +58,38 @@ public class CrontabService : ICrontabService
public async Task<List<CrontabItem>> GetCrontable()
{
var convService = _services.GetRequiredService<IConversationService>();
var conv = await convService.GetConversation("73a9ee27-d597-4739-958f-3bd79760ac8e");
if (conv == null || !conv.States.ContainsKey("cron_expression"))
{
return [];
}
return
[
new CrontabItem
{
Cron = "*/30 * * * * *",
AgentId = BuiltInAgentId.AIAssistant,
Topic = conv.States["topic"],
Cron = conv.States["cron_expression"],
Description = conv.States["description"],
Script = conv.States["script"],
Language = conv.States["language"]
}
];
}
public async Task ScheduledTimeArrived(CrontabItem item)
{
_logger.LogInformation("ScheduledTimeArrived");
await HookEmitter.Emit<ICrontabHook>(_services, async hook =>
_logger.LogDebug($"ScheduledTimeArrived {item}");
var hooks = _services.GetServices<ICrontabHook>();
foreach(var hook in hooks)
{
await hook.OnCronTriggered(item);
}
/*await HookEmitter.Emit<ICrontabHook>(_services, async hook =>
await hook.OnCronTriggered(item)
);
);*/
await Task.Delay(1000 * 10);
}
}

View file

@ -62,7 +62,7 @@ public class CrontabWatcher : BackgroundService
if (matches)
{
_logger.LogInformation($"The current time matches the cron expression {item}");
_logger.LogDebug($"The current time matches the cron expression {item}");
cron.ScheduledTimeArrived(item);
}
}

View file

@ -1,7 +1,13 @@
global using System.Text.Json;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Agents;
global using BotSharp.Abstraction.Plugins;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Functions;
global using BotSharp.Core.Crontab.Services;
global using BotSharp.Core.Crontab.Abstraction;
global using BotSharp.Core.Crontab.Models;

View file

@ -0,0 +1,31 @@
{
"name": "util-crontab-schedule_task",
"description": "Set up a scheduled task",
"parameters": {
"type": "object",
"properties": {
"cron_expression": {
"type": "string",
"description": "cron expression include seconds"
},
"topic": {
"type": "string",
"description": "task topic in 1-3 keywords related to business entities"
},
"description": {
"type": "string",
"description": "task description without cron infromation, should include all key business entities"
},
"script": {
"type": "string",
"description": "task related script, commands or provided function with parameters"
},
"language": {
"type": "string",
"enum": ["sql", "function"],
"description": "script programming language"
}
},
"required": [ "cron_expression", "topic", "description", "script", "language" ]
}
}

View file

@ -0,0 +1 @@
Call schedule_task if user needs to set up a scheduled task with appropriate programming script and language type.

View file

@ -1,24 +0,0 @@
{
"id": "c2o139da-a62a-4355-8605-fdf0ffaca58e",
"name": "Crontab",
"description": "Convert the user-specified schedule into a Cron expression, accurate to the second level.",
"iconUrl": "https://icon-library.com/images/stop-watch-icon/stop-watch-icon-10.jpg",
"type": "task",
"createdDateTime": "2024-12-03T00:00:00Z",
"updatedDateTime": "2024-12-03T00:00:00Z",
"disabled": false,
"isPublic": true,
"profiles": [ "cron" ],
"llmConfig": {
"provider": "openai",
"model": "gpt-4o-mini"
},
"routingRules": [
{
"field": "cron_expression",
"required": true,
"field_type": "string",
"description": "A Cron expression, accurate to the second level."
}
]
}

View file

@ -15,7 +15,7 @@ public static class HookEmitter
{
try
{
logger.LogInformation($"Emit hook action on {action.Method.Name}({hook.GetType().Name})");
logger.LogDebug($"Emit hook action on {action.Method.Name}({hook.GetType().Name})");
action(hook);
if (option.OnlyOnce)
@ -43,7 +43,7 @@ public static class HookEmitter
{
try
{
logger.LogInformation($"Emit hook action on {action.Method.Name}({hook.GetType().Name})");
logger.LogDebug($"Emit hook action on {action.Method.Name}({hook.GetType().Name})");
await action(hook);
if (option.OnlyOnce)

View file

@ -8,7 +8,7 @@
"iconUrl": "https://cdn.iconscout.com/icon/premium/png-256-thumb/route-1613278-1368497.png",
"disabled": false,
"isPublic": true,
"profiles": [ "tool" ],
"profiles": [ "planning", "fallback", "human" ],
"routingRules": [
{
"type": "reasoner",

View file

@ -19,6 +19,7 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core.Crontab\BotSharp.Core.Crontab.csproj" />
<ProjectReference Include="..\..\Infrastructure\BotSharp.OpenAPI\BotSharp.OpenAPI.csproj" />
</ItemGroup>
</Project>

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Crontab.Abstraction;
using BotSharp.Plugin.ChatHub.Hooks;
using Microsoft.Extensions.Configuration;
@ -23,5 +24,6 @@ public class ChatHubPlugin : IBotSharpPlugin
services.AddScoped<IConversationHook, WelcomeHook>();
services.AddScoped<IRoutingHook, StreamingLogHook>();
services.AddScoped<IContentGeneratingHook, StreamingLogHook>();
services.AddScoped<ICrontabHook, ChatHubCrontabHook>();
}
}

View file

@ -0,0 +1,46 @@
using BotSharp.Core.Crontab.Abstraction;
using BotSharp.Core.Crontab.Models;
using Microsoft.AspNetCore.SignalR;
namespace BotSharp.Plugin.ChatHub.Hooks;
public class ChatHubCrontabHook : ICrontabHook
{
private readonly IServiceProvider _services;
private readonly IHubContext<SignalRHub> _chatHub;
private readonly IUserIdentity _user;
private readonly IConversationStorage _storage;
private readonly BotSharpOptions _options;
public ChatHubCrontabHook(IServiceProvider services,
IHubContext<SignalRHub> chatHub,
IUserIdentity user,
IConversationStorage storage,
BotSharpOptions options)
{
_services = services;
_chatHub = chatHub;
_user = user;
_storage = storage;
_options = options;
}
public async Task OnCronTriggered(CrontabItem item)
{
var json = JsonSerializer.Serialize(new ChatResponseModel()
{
ConversationId = item.ConversationId,
MessageId = Guid.NewGuid().ToString(),
Text = item.ExecutionResult,
Function = "",
Sender = new UserViewModel()
{
FirstName = "Crontab",
LastName = "AI",
Role = AgentRole.Assistant
}
}, _options.JsonSerializerOptions);
await _chatHub.Clients.User(item.UserId).SendAsync("OnNotificationGenerated", json);
}
}

View file

@ -1,7 +1,7 @@
{
"id": "282a7128-69a1-44b0-878c-a9159b88f3b9",
"name": "Planner",
"description": "Plan feasible implementation steps for complex user task request",
"description": "Plan feasible implementation steps for complex user task request, including generating sql query",
"type": "task",
"createdDateTime": "2023-08-27T10:39:00Z",
"updatedDateTime": "2023-08-27T14:39:00Z",

View file

@ -15,6 +15,7 @@ Don't run the planning process repeatedly if you have already got the result of
Function verify_dictionary_term CAN'T generate INSERT SQL Statement.
The table name must come from the relevant knowledge. has_found_relevant_knowledge must be true.
Do not introduce your actions or intentions in any way.
Only extract the Data related request, ignore the other request, such as schedule job, make phone call etc.
{% if global_knowledges != empty -%}
=====

View file

@ -107,6 +107,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core.Crontab\BotSharp.Core.Crontab.csproj" />
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
</ItemGroup>

View file

@ -0,0 +1,47 @@
using BotSharp.Core.Crontab.Abstraction;
using BotSharp.Core.Crontab.Models;
using Microsoft.EntityFrameworkCore.Query;
namespace BotSharp.Plugin.SqlDriver.Hooks;
public class SqlDriverCrontabHook : ICrontabHook
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public SqlDriverCrontabHook(IServiceProvider services, ILogger<SqlDriverCrontabHook> logger)
{
_services = services;
_logger = logger;
}
public async Task OnCronTriggered(CrontabItem item)
{
if (item.Language != "sql")
{
return;
}
_logger.LogWarning($"Crontab item triggered: {item.Topic}. Run {item.Language}: {item.Script}");
var conv = _services.GetRequiredService<IConversationService>();
conv.SetConversationId("73a9ee27-d597-4739-958f-3bd79760ac8e", []);
var message = new RoleDialogModel(AgentRole.User, $"Run the query")
{
FunctionName = "sql_select",
FunctionArgs = JsonSerializer.Serialize(new SqlStatement
{
Statement = item.Script,
Reason = item.Description
})
};
var routing = _services.GetRequiredService<IRoutingService>();
routing.Context.Push("ec46f15b-8790-400f-a37f-1e7995b7d6e2");
await routing.InvokeFunction("sql_select", message);
item.ConversationId = conv.ConversationId;
item.AgentId = BuiltInAgentId.SqlDriver;
item.UserId = "41021346";
item.ExecutionResult = message.Content;
}
}

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Planning;
using BotSharp.Core.Crontab.Abstraction;
namespace BotSharp.Plugin.SqlDriver;
@ -29,5 +30,6 @@ public class SqlDriverPlugin : IBotSharpPlugin
services.AddScoped<IAgentHook, SqlDriverAgentHook>();
services.AddScoped<IConversationHook, SqlDriverConversationHook>();
services.AddScoped<IAgentUtilityHook, SqlUtilityHook>();
services.AddScoped<ICrontabHook, SqlDriverCrontabHook>();
}
}

View file

@ -1,7 +1,7 @@
{
"id": "beda4c12-e1ec-4b4b-b328-3df4a6687c4f",
"name": "SQL Driver",
"description": "Transfer to this Agent is allowed only when executable SQL statements are provided in the context.",
"description": "Transfer to this Agent only when executable SQL statements are explicitly provided in the context.",
"iconUrl": "https://cdn-icons-png.flaticon.com/512/3161/3161158.png",
"type": "task",
"createdDateTime": "2023-11-15T13:49:00Z",
@ -11,14 +11,14 @@
"profiles": [ "database" ],
"llmConfig": {
"provider": "openai",
"model": "gpt-4o"
"model": "gpt-4o-2024-11-20"
},
"routingRules": [
{
"field": "sql_statement",
"required": true,
"field_type": "string",
"description": "SQL statement provided in the context"
"description": "SQL statement explicitly provided in the context."
}
]
}

View file

@ -1,12 +1,12 @@
{
"name": "sql_select",
"description": "Execute the reporting related query in the database and get the result",
"description": "Execute the provided query in the database and get the result.",
"parameters": {
"type": "object",
"properties": {
"sql_statement": {
"type": "string",
"description": "SQL statement with SELECT"
"description": "SQL statement with SELECT provided in context."
},
"reason": {
"type": "string",