refine utility structure

This commit is contained in:
Jicheng Lu 2025-05-28 16:16:48 -05:00
parent db542b7e90
commit 1792da38ef
28 changed files with 296 additions and 162 deletions

View file

@ -36,7 +36,7 @@ public interface IAgentService
FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def);
bool RenderUtility(Agent agent, AgentUtility utility);
bool RenderVisibility(string? visibilityExpression, Dictionary<string, object> dict);
/// <summary>
/// Get agent detail without trigger any hook.

View file

@ -2,64 +2,68 @@ namespace BotSharp.Abstraction.Agents.Models;
public class AgentUtility
{
public string Category { get; set; }
public string Name { get; set; }
public bool Disabled { get; set; }
[JsonPropertyName("visibility_expression")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? VisibilityExpression { get; set; }
public IEnumerable<UtilityFunction> Functions { get; set; } = [];
public IEnumerable<UtilityTemplate> Templates { get; set; } = [];
public IEnumerable<UtilityItem> Items { get; set; } = [];
public AgentUtility()
{
}
public AgentUtility(
string name,
IEnumerable<UtilityFunction>? functions = null,
IEnumerable<UtilityTemplate>? templates = null)
{
Name = name;
Functions = functions ?? [];
Templates = templates ?? [];
}
public override string ToString()
{
return Name;
return $"{Category}-{Name}";
}
}
public class UtilityFunction : UtilityBase
public class UtilityItem
{
public UtilityFunction()
{
[JsonPropertyName("function_name")]
public string FunctionName { get; set; } = string.Empty;
}
[JsonPropertyName("template_name")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? TemplateName { get; set; }
public UtilityFunction(string name)
{
Name = name;
}
[JsonPropertyName("visibility_expression")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? VisibilityExpression { get; set; }
}
public class UtilityTemplate : UtilityBase
{
public UtilityTemplate()
{
//public class UtilityFunction : UtilityBase
//{
// public UtilityFunction()
// {
}
// }
public UtilityTemplate(string name)
{
Name = name;
}
}
// public UtilityFunction(string name)
// {
// Name = name;
// }
//}
public class UtilityBase
{
public string Name { get; set; }
}
//public class UtilityTemplate : UtilityBase
//{
// public UtilityTemplate()
// {
// }
// public UtilityTemplate(string name)
// {
// Name = name;
// }
//}
//public class UtilityBase
//{
// public string Name { get; set; }
//}

View file

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

View file

@ -15,9 +15,19 @@ public class CrontabUtilityHook : IAgentUtilityHook
{
new AgentUtility
{
Category = "crontab",
Name = UtilityName.ScheduleTask,
Functions = [new(SCHEDULE_TASK_FN), new(TASK_WAIT_FN)],
Templates = [new($"{SCHEDULE_TASK_FN}.fn")]
Items = [
new UtilityItem
{
FunctionName = SCHEDULE_TASK_FN,
TemplateName = $"{SCHEDULE_TASK_FN}.fn"
},
new UtilityItem
{
FunctionName = TASK_WAIT_FN
},
]
}
};

View file

@ -63,21 +63,33 @@ public class BasicAgentHook : AgentHookBase
}
var agentService = _services.GetRequiredService<IAgentService>();
var innerUtilities = utilities!.Where(x =>
var innerUtilities = utilities!.Where(x => !string.IsNullOrEmpty(x.Name) && !x.Disabled).ToList();
var functionNames = new List<string>();
var templateNames = new List<string>();
foreach (var utility in innerUtilities)
{
var isVisible = !string.IsNullOrEmpty(x.Name) && !x.Disabled;
return isVisible && agentService.RenderUtility(agent, x);
}).ToList();
var isVisible = agentService.RenderVisibility(utility.VisibilityExpression, agent.TemplateDict);
if (!isVisible || utility.Items.IsNullOrEmpty()) continue;
var functionNames = innerUtilities.SelectMany(x => x.Functions)
.Where(x => !string.IsNullOrEmpty(x.Name) && x.Name.StartsWith(UTIL_PREFIX))
.Select(x => x.Name)
.Distinct().ToList();
var templateNames = innerUtilities.SelectMany(x => x.Templates)
.Where(x => !string.IsNullOrEmpty(x.Name) && x.Name.StartsWith(UTIL_PREFIX))
.Select(x => x.Name)
.Distinct().ToList();
foreach (var item in utility.Items)
{
isVisible = agentService.RenderVisibility(item.VisibilityExpression, agent.TemplateDict);
if (!isVisible) continue;
return (functionNames, templateNames);
if (item.FunctionName?.StartsWith(UTIL_PREFIX) == true)
{
functionNames.Add(item.FunctionName);
}
if (item.TemplateName?.StartsWith(UTIL_PREFIX) == true)
{
templateNames.Add(item.TemplateName);
}
}
}
return (functionNames.Distinct(), templateNames.Distinct());
}
}

View file

@ -46,12 +46,7 @@ public partial class AgentService
if (!string.IsNullOrWhiteSpace(def.VisibilityExpression))
{
var render = _services.GetRequiredService<ITemplateRender>();
var result = render.Render(def.VisibilityExpression, new Dictionary<string, object>
{
{ "states", agent.TemplateDict }
});
isRender = isRender && result == "visible";
isRender = RenderVisibility(def.VisibilityExpression, agent.TemplateDict);
}
return isRender;
@ -76,12 +71,7 @@ public partial class AgentService
if (node.TryGetProperty(visibleExpress, out var element))
{
var expression = element.GetString();
var render = _services.GetRequiredService<ITemplateRender>();
var result = render.Render(expression, new Dictionary<string, object>
{
{ "states", agent.TemplateDict }
});
matched = result == "visible";
matched = RenderVisibility(expression, agent.TemplateDict);
}
if (matched)
@ -138,19 +128,19 @@ public partial class AgentService
return content;
}
public bool RenderUtility(Agent agent, AgentUtility utility)
public bool RenderVisibility(string? visibilityExpression, Dictionary<string, object> dict)
{
if (string.IsNullOrWhiteSpace(utility?.VisibilityExpression))
if (string.IsNullOrWhiteSpace(visibilityExpression))
{
return true;
}
var render = _services.GetRequiredService<ITemplateRender>();
var result = render.Render(utility.VisibilityExpression, new Dictionary<string, object>
var result = render.Render(visibilityExpression, new Dictionary<string, object>
{
{ "states", agent.TemplateDict }
{ "states", dict ?? [] }
});
return result == "visible";
return result.IsEqualTo("visible");
}
}

View file

@ -9,9 +9,14 @@ public class InstructUtilityHook : IAgentUtilityHook
{
utilities.Add(new AgentUtility
{
Name = "instruct.template",
Functions = [new($"{EXECUTE_TEMPLATE}")],
Templates = [new($"{EXECUTE_TEMPLATE}.fn")]
Category = "instruct",
Name = "template",
Items = [
new UtilityItem {
FunctionName = $"{EXECUTE_TEMPLATE}",
TemplateName = $"{EXECUTE_TEMPLATE}.fn"
}
]
});
}
}

View file

@ -10,9 +10,20 @@ public class RoutingUtilityHook : IAgentUtilityHook
{
utilities.Add(new AgentUtility
{
Category = "routing",
Name = "routing.tools",
Functions = [new($"{REDIRECT_TO_AGENT}"), new($"{FALLBACK_TO_ROUTER}")],
Templates = [new($"{REDIRECT_TO_AGENT}.fn"), new($"{FALLBACK_TO_ROUTER}.fn")]
Items = [
new UtilityItem
{
FunctionName = $"{REDIRECT_TO_AGENT}",
TemplateName = $"{REDIRECT_TO_AGENT}.fn"
},
new UtilityItem
{
FunctionName = $"{FALLBACK_TO_ROUTER}",
TemplateName = $"{FALLBACK_TO_ROUTER}.fn"
}
]
});
}
}

View file

@ -167,7 +167,9 @@ public class AgentController : ControllerBase
{
hook.AddUtilities(utilities);
}
return utilities.Where(x => !string.IsNullOrWhiteSpace(x.Name)).OrderBy(x => x.Name).ToList();
return utilities.Where(x => !string.IsNullOrWhiteSpace(x.Category)
&& !string.IsNullOrWhiteSpace(x.Name)
&& !x.Items.IsNullOrEmpty()).ToList();
}
[HttpGet("/agent/labels")]

View file

@ -2,5 +2,5 @@ namespace BotSharp.Plugin.AudioHandler.Enums;
public class UtilityName
{
public const string AudioHandler = "audio.audio-handler";
public const string AudioHandler = "audio-handler";
}

View file

@ -9,9 +9,15 @@ public class AudioHandlerUtilityHook : IAgentUtilityHook
{
var utility = new AgentUtility
{
Category = "audio",
Name = UtilityName.AudioHandler,
Functions = [new(HANDLER_AUDIO)],
Templates = [new($"{HANDLER_AUDIO}.fn")]
Items = [
new UtilityItem
{
FunctionName = HANDLER_AUDIO,
TemplateName = $"{HANDLER_AUDIO}.fn"
}
]
};
utilities.Add(utility);

View file

@ -2,5 +2,5 @@ namespace BotSharp.Plugin.EmailHandler.Enums;
public class UtilityName
{
public const string EmailHandler = "email.email-handler";
public const string EmailHandler = "email-handler";
}

View file

@ -13,9 +13,20 @@ public class EmailHandlerUtilityHook : IAgentUtilityHook
{
var utility = new AgentUtility
{
Category = "email",
Name = UtilityName.EmailHandler,
Functions = [new(EMAIL_READER_FN), new(EMAIL_SENDER_FN)],
Templates = [new($"{EMAIL_READER_FN}.fn"), new($"{EMAIL_SENDER_FN}.fn")]
Items = [
new UtilityItem
{
FunctionName = EMAIL_READER_FN,
TemplateName = $"{EMAIL_READER_FN}.fn"
},
new UtilityItem
{
FunctionName = EMAIL_SENDER_FN,
TemplateName = $"{EMAIL_SENDER_FN}.fn"
}
]
};
utilities.Add(utility);

View file

@ -2,8 +2,8 @@ namespace BotSharp.Plugin.FileHandler.Enums;
public class UtilityName
{
public const string ImageGenerator = "file.image-generator";
public const string ImageReader = "file.image-reader";
public const string ImageEditor = "file.image-editor";
public const string PdfReader = "file.pdf-reader";
public const string ImageGenerator = "image-generator";
public const string ImageReader = "image-reader";
public const string ImageEditor = "image-editor";
public const string PdfReader = "pdf-reader";
}

View file

@ -13,27 +13,50 @@ public class FileHandlerUtilityHook : IAgentUtilityHook
{
new AgentUtility
{
Category = "file",
Name = UtilityName.ImageGenerator,
Functions = [new(GENERATE_IMAGE_FN)],
Templates = [new($"{GENERATE_IMAGE_FN}.fn")]
Items = [
new UtilityItem
{
FunctionName = GENERATE_IMAGE_FN,
TemplateName = $"{GENERATE_IMAGE_FN}.fn"
}
]
},
new AgentUtility
{
Category = "file",
Name = UtilityName.ImageReader,
Functions = [new(READ_IMAGE_FN)],
Templates = [new($"{READ_IMAGE_FN}.fn")]
Items = [
new UtilityItem
{
FunctionName = READ_IMAGE_FN,
TemplateName = $"{READ_IMAGE_FN}.fn"
}
]
},
new AgentUtility
{
Name = UtilityName.ImageEditor,
Functions = [new(EDIT_IMAGE_FN)],
Templates = [new($"{EDIT_IMAGE_FN}.fn")]
Items = [
new UtilityItem
{
FunctionName = EDIT_IMAGE_FN,
TemplateName = $"{EDIT_IMAGE_FN}.fn"
}
]
},
new AgentUtility
{
Category = "file",
Name = UtilityName.PdfReader,
Functions = [new(READ_PDF_FN)],
Templates = [new($"{READ_PDF_FN}.fn")]
Items = [
new UtilityItem
{
FunctionName = READ_PDF_FN,
TemplateName = $"{READ_PDF_FN}.fn"
}
]
}
};

View file

@ -2,5 +2,5 @@ namespace BotSharp.Plugin.HttpHandler.Enums;
public class UtilityName
{
public const string HttpHandler = "http.http-handler";
public const string HttpHandler = "http-handler";
}

View file

@ -11,9 +11,15 @@ public class HttpHandlerUtilityHook : IAgentUtilityHook
{
var utility = new AgentUtility
{
Category = "http",
Name = UtilityName.HttpHandler,
Functions = [new(HTTP_HANDLER_FN)],
Templates = [new($"{HTTP_HANDLER_FN}.fn")]
Items = [
new UtilityItem
{
FunctionName = HTTP_HANDLER_FN,
TemplateName = $"{HTTP_HANDLER_FN}.fn"
}
]
};
utilities.Add(utility);

View file

@ -2,5 +2,5 @@ namespace BotSharp.Plugin.KnowledgeBase.Enum;
public class UtilityName
{
public const string KnowledgeRetrieval = "kg.knowledge-base";
public const string KnowledgeRetrieval = "knowledge-base";
}

View file

@ -9,9 +9,15 @@ public class KnowledgeBaseUtilityHook : IAgentUtilityHook
{
var utility = new AgentUtility
{
Category = "knowledge",
Name = UtilityName.KnowledgeRetrieval,
Functions = [new(KNOWLEDGE_RETRIEVAL_FN)],
Templates = [new($"{KNOWLEDGE_RETRIEVAL_FN}.fn")]
Items = [
new UtilityItem
{
FunctionName = KNOWLEDGE_RETRIEVAL_FN,
TemplateName = $"{KNOWLEDGE_RETRIEVAL_FN}.fn"
}
]
};
utilities.Add(utility);

View file

@ -5,21 +5,26 @@ namespace BotSharp.Plugin.MongoStorage.Models;
[BsonIgnoreExtraElements(Inherited = true)]
public class AgentUtilityMongoElement
{
public string Category { get; set; } = default!;
public string Name { get; set; } = default!;
public bool Disabled { get; set; }
public string? VisibilityExpression { get; set; }
public List<UtilityFunctionMongoElement> Functions { get; set; } = [];
public List<UtilityTemplateMongoElement> Templates { get; set; } = [];
public List<AgentUtilityItemMongoElement> Items { get; set; } = [];
public static AgentUtilityMongoElement ToMongoElement(AgentUtility utility)
{
return new AgentUtilityMongoElement
{
Category = utility.Category,
Name = utility.Name,
Disabled = utility.Disabled,
VisibilityExpression = utility.VisibilityExpression,
Functions = utility.Functions?.Select(x => new UtilityFunctionMongoElement(x.Name))?.ToList() ?? [],
Templates = utility.Templates?.Select(x => new UtilityTemplateMongoElement(x.Name))?.ToList() ?? []
Items = utility.Items?.Select(x => new AgentUtilityItemMongoElement
{
FunctionName = x.FunctionName,
TemplateName = x.TemplateName,
VisibilityExpression = x.VisibilityExpression
})?.ToList() ?? []
};
}
@ -27,21 +32,23 @@ public class AgentUtilityMongoElement
{
return new AgentUtility
{
Category = utility.Category,
Name = utility.Name,
Disabled = utility.Disabled,
VisibilityExpression = utility.VisibilityExpression,
Functions = utility.Functions?.Select(x => new UtilityFunction(x.Name))?.ToList() ?? [],
Templates = utility.Templates?.Select(x => new UtilityTemplate(x.Name))?.ToList() ?? []
Items = utility.Items?.Select(x => new UtilityItem
{
FunctionName = x.FunctionName,
TemplateName = x.TemplateName,
VisibilityExpression = x.VisibilityExpression
})?.ToList() ?? [],
};
}
}
public class UtilityFunctionMongoElement(string name)
public class AgentUtilityItemMongoElement
{
public string Name { get; set; } = name;
}
public class UtilityTemplateMongoElement(string name)
{
public string Name { get; set; } = name;
public string FunctionName { get; set; } = string.Empty;
public string? TemplateName { get; set; }
public string? VisibilityExpression { get; set; }
}

View file

@ -2,5 +2,5 @@ namespace BotSharp.Plugin.Planner.Enums;
public class UtilityName
{
public const string TwoStagePlanner = "planner.two-stage-planner";
public const string TwoStagePlanner = "two-stage-planner";
}

View file

@ -10,16 +10,24 @@ public class TwoStagingPlannerUtilityHook : IAgentUtilityHook
{
var utility = new AgentUtility
{
Category = "planner",
Name = UtilityName.TwoStagePlanner,
Functions = [
new(PRIMARY_STAGE_FN),
new(SECONDARY_STAGE_FN),
new(SUMMARY_FN)
],
Templates = [
new($"{PRIMARY_STAGE_FN}.fn"),
new($"{SECONDARY_STAGE_FN}.fn"),
new($"{SUMMARY_FN}.fn")
Items = [
new UtilityItem
{
FunctionName = PRIMARY_STAGE_FN,
TemplateName = $"{PRIMARY_STAGE_FN}.fn"
},
new UtilityItem
{
FunctionName = SECONDARY_STAGE_FN,
TemplateName = $"{SECONDARY_STAGE_FN}.fn"
},
new UtilityItem
{
FunctionName = SUMMARY_FN,
TemplateName = $"{SUMMARY_FN}.fn"
}
]
};

View file

@ -8,9 +8,15 @@ public class InterpreterUtilityHook : IAgentUtilityHook
{
var utility = new AgentUtility()
{
Category = "coding",
Name = UtilityName.PythonInterpreter,
Functions = [new(FUNCTION_NAME)],
Templates = [new($"{FUNCTION_NAME}.fn")]
Items = [
new UtilityItem
{
FunctionName = FUNCTION_NAME,
TemplateName = $"{FUNCTION_NAME}.fn"
}
]
};
utilities.Add(utility);

View file

@ -14,18 +14,24 @@ public class SqlUtilityHook : IAgentUtilityHook
{
new AgentUtility
{
Name = "db.tools",
Functions =
[
new(SQL_TABLE_DEFINITION_FN),
new(VERIFY_DICTIONARY_TERM_FN),
new(SQL_SELECT_FN),
],
Templates =
[
new($"{VERIFY_DICTIONARY_TERM_FN}.fn"),
new($"{SQL_TABLE_DEFINITION_FN}.fn"),
new($"{SQL_EXECUTOR_FN}.fn")
Category = "database",
Name = "sql.tools",
Items = [
new UtilityItem
{
FunctionName = SQL_TABLE_DEFINITION_FN,
TemplateName = $"{SQL_TABLE_DEFINITION_FN}.fn"
},
new UtilityItem
{
FunctionName = VERIFY_DICTIONARY_TERM_FN,
TemplateName = $"{VERIFY_DICTIONARY_TERM_FN}.fn"
},
new UtilityItem
{
FunctionName = SQL_SELECT_FN,
TemplateName = $"{SQL_EXECUTOR_FN}.fn"
}
]
}
};

View file

@ -2,6 +2,6 @@ namespace BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.Enums
{
public class UtilityName
{
public const string OutboundPhoneCall = "phone.twilio-phone-call";
public const string OutboundPhoneCall = "twilio-phone-call";
}
}

View file

@ -16,17 +16,29 @@ public class OutboundPhoneCallHandlerUtilityHook : IAgentUtilityHook
{
var utility = new AgentUtility
{
Category = "phone",
Name = UtilityName.OutboundPhoneCall,
Functions =
[
new($"{OUTBOUND_PHONE_CALL_FN}"),
new($"{TRANSFER_PHONE_CALL_FN}"),
new($"{HANGUP_PHONE_CALL_FN}"),
new($"{TEXT_MESSAGE_FN}"),
new($"{LEAVE_VOICEMAIL_FN}")
],
Templates =
[
Items = [
new UtilityItem
{
FunctionName = OUTBOUND_PHONE_CALL_FN
},
new UtilityItem
{
FunctionName = TRANSFER_PHONE_CALL_FN
},
new UtilityItem
{
FunctionName = HANGUP_PHONE_CALL_FN
},
new UtilityItem
{
FunctionName = TEXT_MESSAGE_FN
},
new UtilityItem
{
FunctionName = LEAVE_VOICEMAIL_FN
}
]
};

View file

@ -14,18 +14,27 @@ public class WebUtilityHook : IAgentUtilityHook
{
new AgentUtility
{
Name = "web.tools",
Functions =
[
new(CLOSE_BROWSER_FN),
new(GO_TO_PAGE_FN),
new(LOCATE_ELEMENT_FN),
new(ACTION_ON_ELEMENT_FN)
],
Templates =
[
new($"{GO_TO_PAGE_FN}.fn"),
new($"{ACTION_ON_ELEMENT_FN}.fn")
Category = "web",
Name = "browser.tools",
Items = [
new UtilityItem
{
FunctionName = GO_TO_PAGE_FN,
TemplateName = $"{GO_TO_PAGE_FN}.fn"
},
new UtilityItem
{
FunctionName = ACTION_ON_ELEMENT_FN,
TemplateName = $"{ACTION_ON_ELEMENT_FN}.fn"
},
new UtilityItem
{
FunctionName = LOCATE_ELEMENT_FN
},
new UtilityItem
{
FunctionName = CLOSE_BROWSER_FN
}
]
}
};

View file

@ -62,7 +62,7 @@ namespace BotSharp.Plugin.Google.Core
return def.Parameters;
}
public bool RenderUtility(Agent agent, AgentUtility utility)
public bool RenderVisibility(string? visibilityExpression, Dictionary<string, object> dict)
{
return true;
}