Merge pull request #513 from iceljc/features/upgrade-open-ai

Features/upgrade open ai
This commit is contained in:
C. Oceania 2024-06-26 14:10:34 -05:00 committed by GitHub
commit 16b1285a86
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
63 changed files with 1125 additions and 370 deletions

View file

@ -4,5 +4,4 @@ public class AgentTool
{
public const string FileAnalyzer = "file-analyzer";
public const string ImageGenerator = "image-generator";
public const string HttpHandler = "http-handler";
}

View file

@ -0,0 +1,6 @@
namespace BotSharp.Abstraction.Agents;
public interface IAgentToolHook
{
void AddTools(List<string> tools);
}

View file

@ -29,4 +29,6 @@ public interface IBotSharpFileService
/// <param name="data"></param>
/// <returns></returns>
(string, byte[]) GetFileInfoFromData(string data);
string GetFileContentType(string filePath);
}

View file

@ -86,10 +86,8 @@ public partial class AgentService
foreach (var file in Directory.GetFiles(templateDir))
{
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
var splitIdx = fileName.LastIndexOf(".");
var name = fileName.Substring(0, splitIdx);
var extension = fileName.Substring(splitIdx + 1);
var name = Path.GetFileNameWithoutExtension(file);
var extension = Path.GetExtension(file).Substring(1);
if (extension.IsEqualTo(_agentSettings.TemplateFormat))
{
var content = File.ReadAllText(file);
@ -102,11 +100,28 @@ public partial class AgentService
private List<FunctionDef> FetchFunctionsFromFile(string fileDir)
{
var file = Path.Combine(fileDir, "functions.json");
if (!File.Exists(file)) return new List<FunctionDef>();
var functions = new List<FunctionDef>();
var functionDir = Path.Combine(fileDir, "functions");
var functionsJson = File.ReadAllText(file);
var functions = JsonSerializer.Deserialize<List<FunctionDef>>(functionsJson, _options);
if (!Directory.Exists(functionDir)) return functions;
foreach (var file in Directory.GetFiles(functionDir))
{
try
{
var extension = Path.GetExtension(file).Substring(1);
if (extension != "json") continue;
var json = File.ReadAllText(file);
var function = JsonSerializer.Deserialize<FunctionDef>(json, _options);
functions.Add(function);
}
catch
{
continue;
}
}
return functions;
}

View file

@ -57,11 +57,13 @@ public partial class AgentService : IAgentService
public IEnumerable<string> GetAgentTools()
{
var tools = typeof(AgentTool).GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(f => f.IsLiteral && f.FieldType == typeof(string))
.Select(x => x.GetRawConstantValue()?.ToString())
.ToList();
var tools = new List<string>();
return tools;
var hooks = _services.GetServices<IAgentToolHook>();
foreach (var hook in hooks)
{
hook.AddTools(tools);
}
return tools.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().OrderBy(x => x).ToList();
}
}

View file

@ -48,10 +48,11 @@
<ItemGroup>
<None Remove="data\agents\00000000-0000-0000-0000-000000000000\agent.json" />
<None Remove="data\agents\00000000-0000-0000-0000-000000000000\instruction.liquid" />
<None Remove="data\agents\00000000-0000-0000-0000-000000000000\functions.json" />
<None Remove="data\agents\00000000-0000-0000-0000-000000000000\templates\load_attachment_prompt.liquid" />
<None Remove="data\agents\00000000-0000-0000-0000-000000000000\functions\load_attachment.json" />
<None Remove="data\agents\00000000-0000-0000-0000-000000000000\templates\load_attachment.fn.liquid" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\agent.json" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions.json" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions\human_intervention_needed.json" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\instruction.liquid" />
<None Remove="data\agents\01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d\agent.json" />
<None Remove="data\agents\01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d\instruction.liquid" />
@ -81,10 +82,13 @@
<Content Include="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\agent.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\instruction.liquid">
<Content Include="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions\human_intervention_needed.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d\agent.json">
@ -156,10 +160,10 @@
<Content Include="data\agents\00000000-0000-0000-0000-000000000000\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\00000000-0000-0000-0000-000000000000\functions.json">
<Content Include="data\agents\00000000-0000-0000-0000-000000000000\functions\load_attachment.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\00000000-0000-0000-0000-000000000000\templates\load_attachment_prompt.liquid">
<Content Include="data\agents\00000000-0000-0000-0000-000000000000\templates\load_attachment.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\plugins\config.json">

View file

@ -66,8 +66,7 @@ public partial class BotSharpFileService : IBotSharpFileService
return (contentType, Convert.FromBase64String(base64Str));
}
#region Private methods
private string GetFileContentType(string filePath)
public string GetFileContentType(string filePath)
{
string contentType;
var provider = new FileExtensionContentTypeProvider();
@ -79,6 +78,7 @@ public partial class BotSharpFileService : IBotSharpFileService
return contentType;
}
#region Private methods
private bool ExistDirectory(string? dir)
{
return !string.IsNullOrEmpty(dir) && Directory.Exists(dir);

View file

@ -17,5 +17,6 @@ public class FilePlugin : IBotSharpPlugin
services.AddScoped<IBotSharpFileService, BotSharpFileService>();
services.AddScoped<IAgentHook, AttachmentProcessingHook>();
services.AddScoped<IAgentToolHook, FileAnalyzerToolHook>();
}
}

View file

@ -19,8 +19,8 @@ public class AttachmentProcessingHook : AgentHookBase
if (isConvMode && isEnabled)
{
var (prompt, loadAttachmentFn) = GetLoadAttachmentFn();
if (loadAttachmentFn != null)
var (prompt, fn) = GetPromptAndFunction();
if (fn != null)
{
if (!string.IsNullOrWhiteSpace(prompt))
{
@ -29,11 +29,11 @@ public class AttachmentProcessingHook : AgentHookBase
if (agent.Functions == null)
{
agent.Functions = new List<FunctionDef> { loadAttachmentFn };
agent.Functions = new List<FunctionDef> { fn };
}
else
{
agent.Functions.Add(loadAttachmentFn);
agent.Functions.Add(fn);
}
}
}
@ -41,13 +41,13 @@ public class AttachmentProcessingHook : AgentHookBase
base.OnAgentLoaded(agent);
}
private (string, FunctionDef?) GetLoadAttachmentFn()
private (string, FunctionDef?) GetPromptAndFunction()
{
var fnName = "load_attachment";
var fn = "load_attachment";
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.GetAgent(TOOL_ASSISTANT);
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{fnName}_prompt"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(fnName));
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{fn}.fn"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(fn));
return (prompt, loadAttachmentFn);
}
}

View file

@ -0,0 +1,10 @@
namespace BotSharp.Core.Files.Hooks;
public class FileAnalyzerToolHook : IAgentToolHook
{
public void AddTools(List<string> tools)
{
tools.Add(AgentTool.FileAnalyzer);
}
}

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Routing.Models;
using System.IO;
using System.Threading;
namespace BotSharp.Core.Repository
{
@ -194,11 +195,24 @@ namespace BotSharp.Core.Repository
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
var functionFile = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir,
agentId, AGENT_FUNCTIONS_FILE);
var functionDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir,
agentId, AGENT_FUNCTIONS_FOLDER);
var functionText = JsonSerializer.Serialize(inputFunctions, _options);
File.WriteAllText(functionFile, functionText);
if (Directory.Exists(functionDir))
{
Directory.Delete(functionDir, true);
}
Directory.CreateDirectory(functionDir);
foreach (var func in inputFunctions)
{
if (string.IsNullOrWhiteSpace(func.Name)) continue;
var text = JsonSerializer.Serialize(func, _options);
var file = Path.Combine(functionDir, $"{func.Name}.json");
File.WriteAllText(file, text);
Thread.Sleep(200);
}
}
private void UpdateAgentTemplates(string agentId, List<AgentTemplate> templates)
@ -208,7 +222,7 @@ namespace BotSharp.Core.Repository
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
var templateDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "templates");
var templateDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_TEMPLATES_FOLDER);
if (!Directory.Exists(templateDir))
{
@ -234,7 +248,7 @@ namespace BotSharp.Core.Repository
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
var responseDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "responses");
var responseDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_RESPONSES_FOLDER);
if (!Directory.Exists(responseDir))
{
Directory.CreateDirectory(responseDir);
@ -305,7 +319,7 @@ namespace BotSharp.Core.Repository
public List<string> GetAgentResponses(string agentId, string prefix, string intent)
{
var responses = new List<string>();
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "responses");
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_RESPONSES_FOLDER);
if (!Directory.Exists(dir)) return responses;
foreach (var file in Directory.GetFiles(dir))
@ -399,7 +413,7 @@ namespace BotSharp.Core.Repository
public string GetAgentTemplate(string agentId, string templateName)
{
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "templates");
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_TEMPLATES_FOLDER);
if (!Directory.Exists(dir)) return string.Empty;
foreach (var file in Directory.GetFiles(dir))
@ -421,7 +435,7 @@ namespace BotSharp.Core.Repository
{
if (string.IsNullOrEmpty(agentId) || template == null) return false;
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "templates");
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_TEMPLATES_FOLDER);
if (!Directory.Exists(dir)) return false;
var foundTemplate = Directory.GetFiles(dir).FirstOrDefault(f =>
@ -460,7 +474,7 @@ namespace BotSharp.Core.Repository
if (string.IsNullOrEmpty(agentDir)) return false;
// Delete agent user relationships
var usersDir = Path.Combine(_dbSettings.FileRepository, "users");
var usersDir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER);
if (Directory.Exists(usersDir))
{
foreach (var userDir in Directory.GetDirectories(usersDir))

View file

@ -20,7 +20,7 @@ public partial class FileRepository
foreach (var agentDir in Directory.GetDirectories(dir))
{
var taskDir = Path.Combine(agentDir, "tasks");
var taskDir = Path.Combine(agentDir, AGENT_TASKS_FOLDER);
if (!Directory.Exists(taskDir)) continue;
var agentId = agentDir.Split(Path.DirectorySeparatorChar).Last();
@ -84,7 +84,7 @@ public partial class FileRepository
var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId);
if (!Directory.Exists(agentDir)) return null;
var taskDir = Path.Combine(agentDir, "tasks");
var taskDir = Path.Combine(agentDir, AGENT_TASKS_FOLDER);
if (!Directory.Exists(taskDir)) return null;
var taskFile = FindTaskFileById(taskDir, taskId);
@ -106,7 +106,7 @@ public partial class FileRepository
var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, task.AgentId);
if (!Directory.Exists(agentDir)) return;
var taskDir = Path.Combine(agentDir, "tasks");
var taskDir = Path.Combine(agentDir, AGENT_TASKS_FOLDER);
if (!Directory.Exists(taskDir))
{
Directory.CreateDirectory(taskDir);
@ -140,7 +140,7 @@ public partial class FileRepository
var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, task.AgentId);
if (!Directory.Exists(agentDir)) return;
var taskDir = Path.Combine(agentDir, "tasks");
var taskDir = Path.Combine(agentDir, AGENT_TASKS_FOLDER);
if (!Directory.Exists(taskDir)) return;
var taskFile = FindTaskFileById(taskDir, task.Id);
@ -195,7 +195,7 @@ public partial class FileRepository
var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId);
if (!Directory.Exists(agentDir) || taskIds.IsNullOrEmpty()) return false;
var taskDir = Path.Combine(agentDir, "tasks");
var taskDir = Path.Combine(agentDir, AGENT_TASKS_FOLDER);
if (!Directory.Exists(taskDir)) return false;
var deletedTasks = new List<string>();

View file

@ -51,7 +51,7 @@ public partial class FileRepository
{
foreach (var user in _users)
{
var dir = Path.Combine(_dbSettings.FileRepository, "users", user.Id);
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, user.Id);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
@ -69,7 +69,7 @@ public partial class FileRepository
var agents = _userAgents.Where(x => x.UserId == uid).ToList();
if (agents.Any())
{
var dir = Path.Combine(_dbSettings.FileRepository, "users", uid);
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, uid);
var path = Path.Combine(dir, USER_AGENT_FILE);
File.WriteAllText(path, JsonSerializer.Serialize(agents, _options));
}

View file

@ -25,12 +25,12 @@ public partial class FileRepository
{
var userId = Guid.NewGuid().ToString();
user.Id = userId;
var dir = Path.Combine(_dbSettings.FileRepository, "users", userId);
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, userId);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
var path = Path.Combine(dir, "user.json");
var path = Path.Combine(dir, USER_FILE);
File.WriteAllText(path, JsonSerializer.Serialize(user, _options));
}
@ -38,8 +38,8 @@ public partial class FileRepository
{
var user = GetUserById(userId);
user.Verified = true;
var dir = Path.Combine(_dbSettings.FileRepository, "users", user.Id);
var path = Path.Combine(dir, "user.json");
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER, user.Id);
var path = Path.Combine(dir, USER_FILE);
File.WriteAllText(path, JsonSerializer.Serialize(user, _options));
}
}

View file

@ -22,7 +22,6 @@ public partial class FileRepository : IBotSharpRepository
private const string AGENT_FILE = "agent.json";
private const string AGENT_INSTRUCTION_FILE = "instruction";
private const string AGENT_FUNCTIONS_FILE = "functions.json";
private const string AGENT_SAMPLES_FILE = "samples.txt";
private const string USER_FILE = "user.json";
private const string USER_AGENT_FILE = "agents.json";
@ -36,6 +35,11 @@ public partial class FileRepository : IBotSharpRepository
private const string AGENT_TASK_PREFIX = "#metadata";
private const string AGENT_TASK_SUFFIX = "/metadata";
private const string TRANSLATION_MEMORY_FILE = "memory.json";
private const string AGENT_FUNCTIONS_FOLDER = "functions";
private const string AGENT_TEMPLATES_FOLDER = "templates";
private const string AGENT_RESPONSES_FOLDER = "responses";
private const string AGENT_TASKS_FOLDER = "tasks";
private const string USERS_FOLDER = "users";
public FileRepository(
IServiceProvider services,
@ -79,7 +83,7 @@ public partial class FileRepository : IBotSharpRepository
return _users.AsQueryable();
}
var dir = Path.Combine(_dbSettings.FileRepository, "users");
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER);
_users = new List<User>();
if (Directory.Exists(dir))
{
@ -142,7 +146,7 @@ public partial class FileRepository : IBotSharpRepository
return _userAgents.AsQueryable();
}
var dir = Path.Combine(_dbSettings.FileRepository, "users");
var dir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER);
_userAgents = new List<UserAgent>();
if (Directory.Exists(dir))
{
@ -194,11 +198,28 @@ public partial class FileRepository : IBotSharpRepository
private List<FunctionDef> FetchFunctions(string fileDir)
{
var file = Path.Combine(fileDir, AGENT_FUNCTIONS_FILE);
if (!File.Exists(file)) return new List<FunctionDef>();
var functions = new List<FunctionDef>();
var functionDir = Path.Combine(fileDir, AGENT_FUNCTIONS_FOLDER);
var functionsJson = File.ReadAllText(file);
var functions = JsonSerializer.Deserialize<List<FunctionDef>>(functionsJson, _options);
if (!Directory.Exists(functionDir)) return functions;
foreach ( var file in Directory.GetFiles(functionDir))
{
try
{
var extension = Path.GetExtension(file).Substring(1);
if (extension != "json") continue;
var json = File.ReadAllText(file);
var function = JsonSerializer.Deserialize<FunctionDef>(json, _options);
functions.Add(function);
}
catch
{
continue;
}
}
return functions;
}
@ -213,7 +234,7 @@ public partial class FileRepository : IBotSharpRepository
private List<AgentTemplate> FetchTemplates(string fileDir)
{
var templates = new List<AgentTemplate>();
var templateDir = Path.Combine(fileDir, "templates");
var templateDir = Path.Combine(fileDir, AGENT_TEMPLATES_FOLDER);
if (!Directory.Exists(templateDir)) return templates;
foreach (var file in Directory.GetFiles(templateDir))
@ -235,7 +256,7 @@ public partial class FileRepository : IBotSharpRepository
private List<AgentTask> FetchTasks(string fileDir)
{
var tasks = new List<AgentTask>();
var taskDir = Path.Combine(fileDir, "tasks");
var taskDir = Path.Combine(fileDir, AGENT_TASKS_FOLDER);
if (!Directory.Exists(taskDir)) return tasks;
foreach (var file in Directory.GetFiles(taskDir))
@ -252,7 +273,7 @@ public partial class FileRepository : IBotSharpRepository
private List<AgentResponse> FetchResponses(string fileDir)
{
var responses = new List<AgentResponse>();
var responseDir = Path.Combine(fileDir, "responses");
var responseDir = Path.Combine(fileDir, AGENT_RESPONSES_FOLDER);
if (!Directory.Exists(responseDir)) return responses;
foreach (var file in Directory.GetFiles(responseDir))

View file

@ -1,20 +0,0 @@
[
{
"name": "load_attachment",
"description": "If the user's request is related to analyzing files and/or images, you can call this function to analyze files and images.",
"parameters": {
"type": "object",
"properties": {
"user_request": {
"type": "string",
"description": "The request posted by user, which is related to analyzing requested files. User can request for multiple files to process at one time."
},
"file_types": {
"type": "string",
"description": "The file types requested by user to analyze, such as image, png, jpeg, and pdf. There can be multiple file types in a single request. An example output is, 'image,pdf'."
}
},
"required": [ "user_request", "file_types" ]
}
}
]

View file

@ -0,0 +1,18 @@
{
"name": "load_attachment",
"description": "If the user's request is related to analyzing files and/or images, you can call this function to analyze files and images.",
"parameters": {
"type": "object",
"properties": {
"user_request": {
"type": "string",
"description": "The request posted by user, which is related to analyzing requested files. User can request for multiple files to process at one time."
},
"file_types": {
"type": "string",
"description": "The file types requested by user to analyze, such as image, png, jpeg, and pdf. There can be multiple file types in a single request. An example output is, 'image,pdf'."
}
},
"required": [ "user_request", "file_types" ]
}
}

View file

@ -0,0 +1,18 @@
{
"name": "human_intervention_needed",
"description": "If user wants to speak to human customer service.",
"parameters": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "why customer needs customer service."
},
"summary": {
"type": "string",
"description": "the whole conversation summary with important information"
}
},
"required": [ "reason", "summary" ]
}
}

View file

@ -3,6 +3,7 @@ using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Core.Infrastructures;
using BotSharp.OpenAPI.ViewModels.Instructs;
using NetTopologySuite.IO;
namespace BotSharp.OpenAPI.Controllers;

View file

@ -1,10 +1,7 @@
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Plugins;
using BotSharp.Abstraction.Settings;
using BotSharp.Plugin.AzureOpenAI.Providers;
using BotSharp.Plugin.AzureOpenAI.Settings;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace BotSharp.Platform.AzureAi;

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>$(TargetFramework)</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.0.0-beta.2" />
</ItemGroup>
<ItemGroup>

View file

@ -1,21 +1,4 @@
using Azure.AI.OpenAI;
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Files;
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Utilities;
using BotSharp.Plugin.AzureOpenAI.Settings;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using OpenAI.Chat;
namespace BotSharp.Plugin.AzureOpenAI.Providers;
@ -49,22 +32,24 @@ public class ChatCompletionProvider : IChatCompletion
}
var client = ProviderHelper.GetClient(Provider, _model, _services);
var (prompt, chatCompletionsOptions) = PrepareOptions(agent, conversations);
chatCompletionsOptions.DeploymentName = _model;
var response = client.GetChatCompletions(chatCompletionsOptions);
var choice = response.Value.Choices[0];
var message = choice.Message;
var chatClient = client.GetChatClient(_model);
var (prompt, messages, options) = PrepareOptions(agent, conversations);
var response = chatClient.CompleteChat(messages, options);
var value = response.Value;
var reason = value.FinishReason;
var content = value.Content;
var text = content.FirstOrDefault()?.Text ?? string.Empty;
RoleDialogModel responseMessage;
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
if (reason == ChatFinishReason.FunctionCall)
{
responseMessage = new RoleDialogModel(AgentRole.Function, message.Content)
responseMessage = new RoleDialogModel(AgentRole.Function, text)
{
CurrentAgentId = agent.Id,
MessageId = conversations.Last().MessageId,
FunctionName = message.FunctionCall.Name,
FunctionArgs = message.FunctionCall.Arguments
FunctionName = value.FunctionCall.FunctionName,
FunctionArgs = value.FunctionCall.FunctionArguments
};
// Somethings LLM will generate a function name with agent name.
@ -73,28 +58,20 @@ public class ChatCompletionProvider : IChatCompletion
responseMessage.FunctionName = responseMessage.FunctionName.Split('.').Last();
}
}
else if (choice.FinishReason == CompletionsFinishReason.ToolCalls)
else if (reason == ChatFinishReason.ToolCalls)
{
// Add the assistant message with tool calls to the conversation history
// ChatRequestAssistantMessage toolCallHistoryMessage = new(message);
// chatCompletionsOptions.Messages.Add(toolCallHistoryMessage);
// Add a new tool message for each tool call that is resolved
var toolCall = message.ToolCalls.First() as ChatCompletionsFunctionToolCall;
// var toolCallResponseMessage = GetToolCallResponseMessage(toolCall);
// Now make a new request with all the messages thus far, including the original
responseMessage = new RoleDialogModel(AgentRole.Function, message.Content)
var toolCall = value.ToolCalls.FirstOrDefault();
responseMessage = new RoleDialogModel(AgentRole.Function, text)
{
CurrentAgentId = agent.Id,
MessageId = conversations.Last().MessageId,
FunctionName = toolCall.Name,
FunctionArgs = toolCall.Arguments
FunctionName = toolCall?.FunctionName,
FunctionArgs = toolCall?.FunctionArguments
};
}
else
{
responseMessage = new RoleDialogModel(AgentRole.Assistant, message.Content)
responseMessage = new RoleDialogModel(AgentRole.Assistant, text)
{
CurrentAgentId = agent.Id,
MessageId = conversations.Last().MessageId
@ -109,8 +86,8 @@ public class ChatCompletionProvider : IChatCompletion
Prompt = prompt,
Provider = Provider,
Model = _model,
PromptCount = response.Value.Usage.PromptTokens,
CompletionCount = response.Value.Usage.CompletionTokens
PromptCount = response.Value.Usage.InputTokens,
CompletionCount = response.Value.Usage.OutputTokens
});
}
@ -131,14 +108,16 @@ public class ChatCompletionProvider : IChatCompletion
}
var client = ProviderHelper.GetClient(Provider, _model, _services);
var (prompt, chatCompletionsOptions) = PrepareOptions(agent, conversations);
var chatClient = client.GetChatClient(_model);
var (prompt, messages, options) = PrepareOptions(agent, conversations);
chatCompletionsOptions.DeploymentName = _model;
var response = await client.GetChatCompletionsAsync(chatCompletionsOptions);
var choice = response.Value.Choices[0];
var message = choice.Message;
var response = await chatClient.CompleteChatAsync(messages, options);
var value = response.Value;
var reason = value.FinishReason;
var content = value.Content;
var text = content.FirstOrDefault()?.Text ?? string.Empty;
var msg = new RoleDialogModel(AgentRole.Assistant, message.Content)
var msg = new RoleDialogModel(AgentRole.Assistant, text)
{
CurrentAgentId = agent.Id
};
@ -151,20 +130,20 @@ public class ChatCompletionProvider : IChatCompletion
Prompt = prompt,
Provider = Provider,
Model = _model,
PromptCount = response.Value.Usage.PromptTokens,
CompletionCount = response.Value.Usage.CompletionTokens
PromptCount = response.Value.Usage.InputTokens,
CompletionCount = response.Value.Usage.OutputTokens
});
}
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
if (reason == ChatFinishReason.FunctionCall)
{
_logger.LogInformation($"[{agent.Name}]: {message.FunctionCall.Name}({message.FunctionCall.Arguments})");
_logger.LogInformation($"[{agent.Name}]: {value.FunctionCall.FunctionName}({value.FunctionCall.FunctionArguments})");
var funcContextIn = new RoleDialogModel(AgentRole.Function, message.Content)
var funcContextIn = new RoleDialogModel(AgentRole.Function, text)
{
CurrentAgentId = agent.Id,
FunctionName = message.FunctionCall.Name,
FunctionArgs = message.FunctionCall.Arguments
FunctionName = value.FunctionCall.FunctionName,
FunctionArgs = value.FunctionCall.FunctionArguments
};
// Somethings LLM will generate a function name with agent name.
@ -188,37 +167,33 @@ public class ChatCompletionProvider : IChatCompletion
public async Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
{
var client = ProviderHelper.GetClient(Provider, _model, _services);
var (prompt, chatCompletionsOptions) = PrepareOptions(agent, conversations);
chatCompletionsOptions.DeploymentName = _model;
var response = await client.GetChatCompletionsStreamingAsync(chatCompletionsOptions);
var chatClient = client.GetChatClient(_model);
var (prompt, messages, options) = PrepareOptions(agent, conversations);
var response = chatClient.CompleteChatStreamingAsync(messages, options);
string output = "";
await foreach (var choice in response)
{
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
if (choice.FinishReason == ChatFinishReason.FunctionCall)
{
Console.Write(choice.FunctionArgumentsUpdate);
Console.Write(choice.FunctionCallUpdate?.FunctionArgumentsUpdate);
await onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), choice.FunctionArgumentsUpdate));
await onMessageReceived(new RoleDialogModel(AgentRole.Assistant, choice.FunctionCallUpdate?.FunctionArgumentsUpdate));
continue;
}
if (choice.ContentUpdate == null)
continue;
Console.Write(choice.ContentUpdate);
if (choice.ContentUpdate.IsNullOrEmpty()) continue;
_logger.LogInformation(choice.ContentUpdate);
_logger.LogInformation(choice.ContentUpdate[0]?.Text);
await onMessageReceived(new RoleDialogModel(choice.Role.ToString(), choice.ContentUpdate));
output = "";
await onMessageReceived(new RoleDialogModel(choice.Role.ToString(), choice.ContentUpdate[0]?.Text ?? string.Empty));
}
return true;
}
protected (string, ChatCompletionsOptions) PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
protected (string, IEnumerable<ChatMessage>, ChatCompletionOptions) PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
{
var agentService = _services.GetRequiredService<IAgentService>();
var fileService = _services.GetRequiredService<IBotSharpFileService>();
@ -227,75 +202,67 @@ public class ChatCompletionProvider : IChatCompletion
var settings = settingsService.GetSetting(Provider, _model);
var allowMultiModal = settings != null && settings.MultiModal;
var chatCompletionsOptions = new ChatCompletionsOptions();
var messages = new List<ChatMessage>();
var temperature = float.Parse(state.GetState("temperature", "0.0"));
var maxTokens = int.Parse(state.GetState("max_tokens", "1024"));
var options = new ChatCompletionOptions()
{
Temperature = temperature,
MaxTokens = maxTokens
};
foreach (var function in agent.Functions)
{
if (!agentService.RenderFunction(agent, function)) continue;
var property = agentService.RenderFunctionProperty(agent, function);
options.Tools.Add(ChatTool.CreateFunctionTool(
functionName: function.Name,
functionDescription: function.Description,
functionParameters: BinaryData.FromObjectAsJson(property)));
}
if (!string.IsNullOrEmpty(agent.Instruction))
{
var instruction = agentService.RenderedInstruction(agent);
chatCompletionsOptions.Messages.Add(new ChatRequestSystemMessage(instruction));
messages.Add(new SystemChatMessage(instruction));
}
if (!string.IsNullOrEmpty(agent.Knowledges))
{
chatCompletionsOptions.Messages.Add(new ChatRequestSystemMessage(agent.Knowledges));
messages.Add(new SystemChatMessage(agent.Knowledges));
}
var samples = ProviderHelper.GetChatSamples(agent.Samples);
foreach (var message in samples)
foreach (var sample in samples)
{
chatCompletionsOptions.Messages.Add(message.Role == AgentRole.User ?
new ChatRequestUserMessage(message.Content) :
new ChatRequestAssistantMessage(message.Content));
}
foreach (var function in agent.Functions)
{
if (agentService.RenderFunction(agent, function))
{
var property = agentService.RenderFunctionProperty(agent, function);
// legacy function call
/*chatCompletionsOptions.Functions.Add(new FunctionDefinition
{
Name = function.Name,
Description = function.Description,
Parameters = BinaryData.FromObjectAsJson(property)
});*/
// new chat tool
chatCompletionsOptions.Tools.Add(new ChatCompletionsFunctionToolDefinition
{
Name = function.Name,
Description = function.Description,
Parameters = BinaryData.FromObjectAsJson(property)
});
}
messages.Add(sample.Role == AgentRole.User ? new UserChatMessage(sample.Content) : new AssistantChatMessage(sample.Content));
}
foreach (var message in conversations)
{
if (message.Role == ChatRole.Function)
if (message.Role == AgentRole.Function)
{
chatCompletionsOptions.Messages.Add(new ChatRequestAssistantMessage(string.Empty)
messages.Add(new AssistantChatMessage(string.Empty)
{
FunctionCall = new FunctionCall(message.FunctionName, message.FunctionArgs ?? String.Empty),
FunctionCall = new ChatFunctionCall(message.FunctionName, message.FunctionArgs ?? string.Empty)
});
chatCompletionsOptions.Messages.Add(new ChatRequestFunctionMessage(message.FunctionName, message.Content));
// chatCompletionsOptions.Messages.Add(new ChatRequestToolMessage(message.Content, message.ToolCallId));
messages.Add(new FunctionChatMessage(message.FunctionName, message.Content));
}
else if (message.Role == ChatRole.User)
else if (message.Role == AgentRole.User)
{
var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content;
var textPart = ChatMessageContentPart.CreateTextMessageContentPart(text);
var chat = new UserChatMessage(textPart)
{
ParticipantName = message.FunctionName
};
ChatRequestUserMessage userMessage = null;
if (allowMultiModal)
{
var chatItems = new List<ChatMessageContentItem>()
{
new ChatMessageTextContentItem(text)
};
if (!message.Files.IsNullOrEmpty())
{
foreach (var file in message.Files)
@ -303,127 +270,97 @@ public class ChatCompletionProvider : IChatCompletion
if (!string.IsNullOrEmpty(file.FileUrl))
{
var uri = new Uri(file.FileUrl);
chatItems.Add(new ChatMessageImageContentItem(uri, ChatMessageImageDetailLevel.Low));
var contentPart = ChatMessageContentPart.CreateImageMessageContentPart(uri, ImageChatMessageContentPartDetail.Low);
chat = new UserChatMessage(textPart, contentPart) { ParticipantName = message.FunctionName };
}
else if (!string.IsNullOrEmpty(file.FileData))
{
var (contentType, bytes) = fileService.GetFileInfoFromData(file.FileData);
using var stream = new MemoryStream(bytes, 0, bytes.Length);
chatItems.Add(new ChatMessageImageContentItem(stream, contentType, ChatMessageImageDetailLevel.Low));
var contentPart = ChatMessageContentPart.CreateImageMessageContentPart(BinaryData.FromBytes(bytes), contentType, ImageChatMessageContentPartDetail.Low);
chat = new UserChatMessage(textPart, contentPart) { ParticipantName = message.FunctionName };
}
else if (!string.IsNullOrEmpty(file.FileStorageUrl))
{
var contentType = fileService.GetFileContentType(file.FileStorageUrl);
using var stream = File.OpenRead(file.FileStorageUrl);
chatItems.Add(new ChatMessageImageContentItem(stream, file.ContentType, ChatMessageImageDetailLevel.Low));
var contentPart = ChatMessageContentPart.CreateImageMessageContentPart(BinaryData.FromStream(stream), contentType, ImageChatMessageContentPartDetail.Low);
chat = new UserChatMessage(textPart, contentPart) { ParticipantName = message.FunctionName };
}
}
}
//if (!string.IsNullOrEmpty(message.ImageUrl))
//{
// var uri = new Uri(message.ImageUrl);
// userMessage.MultimodalContentItems.Add(
// new ChatMessageImageContentItem(uri, ChatMessageImageDetailLevel.Low));
//}
userMessage = new ChatRequestUserMessage(chatItems)
{
// To display Planner name in log
Name = message.FunctionName,
};
}
else
{
userMessage = new ChatRequestUserMessage(text)
{
// To display Planner name in log
Name = message.FunctionName,
};
}
chatCompletionsOptions.Messages.Add(userMessage);
messages.Add(chat);
}
else if (message.Role == ChatRole.Assistant)
else if (message.Role == AgentRole.Assistant)
{
chatCompletionsOptions.Messages.Add(new ChatRequestAssistantMessage(message.Content));
messages.Add(new AssistantChatMessage(message.Content));
}
}
// https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api-a-few-tips-and-tricks-on-controlling-the-creativity-deterministic-output-of-prompt-responses/172683
//var state = _services.GetRequiredService<IConversationStateService>();
var temperature = float.Parse(state.GetState("temperature", "0.0"));
var samplingFactor = float.Parse(state.GetState("sampling_factor", "0.0"));
chatCompletionsOptions.Temperature = temperature;
chatCompletionsOptions.NucleusSamplingFactor = samplingFactor;
chatCompletionsOptions.MaxTokens = int.Parse(state.GetState("max_tokens", "1024"));
// chatCompletionsOptions.FrequencyPenalty = 0;
// chatCompletionsOptions.PresencePenalty = 0;
var prompt = GetPrompt(chatCompletionsOptions);
return (prompt, chatCompletionsOptions);
var prompt = GetPrompt(messages, options);
return (prompt, messages, options);
}
private string GetPrompt(ChatCompletionsOptions chatCompletionsOptions)
private string GetPrompt(IEnumerable<ChatMessage> messages, ChatCompletionOptions options)
{
var prompt = string.Empty;
if (chatCompletionsOptions.Messages.Count > 0)
if (!messages.IsNullOrEmpty())
{
// System instruction
var verbose = string.Join("\r\n", chatCompletionsOptions.Messages
.Where(x => x.Role == AgentRole.System)
.Select(x => x as ChatRequestSystemMessage).Select(x =>
var verbose = string.Join("\r\n", messages
.Select(x => x as SystemChatMessage)
.Where(x => x != null)
.Select(x =>
{
if (!string.IsNullOrEmpty(x.Name))
if (!string.IsNullOrEmpty(x.ParticipantName))
{
// To display Agent name in log
return $"[{x.Name}]: {x.Content}";
return $"[{x.ParticipantName}]: {x.Content.FirstOrDefault()?.Text ?? string.Empty}";
}
return $"{x.Role}: {x.Content}";
return $"{AgentRole.System}: {x.Content.FirstOrDefault()?.Text ?? string.Empty}";
}));
prompt += $"{verbose}\r\n";
prompt += "\r\n[CONVERSATION]";
verbose = string.Join("\r\n", chatCompletionsOptions.Messages
.Where(x => x.Role != AgentRole.System).Select(x =>
verbose = string.Join("\r\n", messages
.Where(x => (x as SystemChatMessage) == null)
.Select(x =>
{
if (x.Role == ChatRole.Function)
var fnMessage = x as FunctionChatMessage;
if (fnMessage != null)
{
var m = x as ChatRequestFunctionMessage;
return $"{m.Role}: {m.Content}";
return $"{AgentRole.Function}: {fnMessage.Content.FirstOrDefault()?.Text ?? string.Empty}";
}
else if (x.Role == ChatRole.User)
var userMessage = x as UserChatMessage;
if (userMessage != null)
{
var m = x as ChatRequestUserMessage;
var content = m.Content ?? string.Join(", ", m.MultimodalContentItems
.Where(m => m is ChatMessageTextContentItem)
.Select(m => (m as ChatMessageTextContentItem)?.Text));
return !string.IsNullOrEmpty(m.Name) && m.Name != "route_to_agent" ?
$"{m.Name}: {content}" :
$"{m.Role}: {content}";
var content = x.Content.FirstOrDefault()?.Text ?? string.Empty;
return !string.IsNullOrEmpty(userMessage.ParticipantName) && userMessage.ParticipantName != "route_to_agent" ?
$"{userMessage.ParticipantName}: {content}" :
$"{AgentRole.User}: {content}";
}
else if (x.Role == ChatRole.Assistant)
var assistMessage = x as AssistantChatMessage;
if (assistMessage != null)
{
var m = x as ChatRequestAssistantMessage;
return m.FunctionCall != null ?
$"{m.Role}: Call function {m.FunctionCall.Name}({m.FunctionCall.Arguments})" :
$"{m.Role}: {m.Content}";
}
else
{
throw new NotImplementedException("Not found role");
return assistMessage.FunctionCall != null ?
$"{AgentRole.Assistant}: Call function {assistMessage.FunctionCall.FunctionName}({assistMessage.FunctionCall.FunctionArguments})" :
$"{AgentRole.Assistant}: {assistMessage.Content.FirstOrDefault()?.Text ?? string.Empty}";
}
return string.Empty;
}));
prompt += $"\r\n{verbose}\r\n";
}
if (chatCompletionsOptions.Tools.Count > 0)
if (!options.Tools.IsNullOrEmpty())
{
var functions = string.Join("\r\n", chatCompletionsOptions.Tools.Select(x =>
var functions = string.Join("\r\n", options.Tools.Select(fn =>
{
var fn = x as ChatCompletionsFunctionToolDefinition;
return $"\r\n{fn.Name}: {fn.Description}\r\n{fn.Parameters}";
return $"\r\n{fn.FunctionName}: {fn.FunctionDescription}\r\n{fn.FunctionParameters}";
}));
prompt += $"\r\n[FUNCTIONS]{functions}\r\n";
}
@ -435,15 +372,4 @@ public class ChatCompletionProvider : IChatCompletion
{
_model = model;
}
ChatRequestToolMessage GetToolCallResponseMessage(ChatCompletionsToolCall toolCall)
{
var functionToolCall = toolCall as ChatCompletionsFunctionToolCall;
// Validate and process the JSON arguments for the function call
string unvalidatedArguments = functionToolCall.Arguments;
var functionResultData = (object)null; // GetYourFunctionResultData(unvalidatedArguments);
// Here, replacing with an example as if returned from "GetYourFunctionResultData"
functionResultData = "31 celsius";
return new ChatRequestToolMessage(functionResultData.ToString(), toolCall.Id);
}
}

View file

@ -1,17 +1,4 @@
using Azure.AI.OpenAI;
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Plugin.AzureOpenAI.Settings;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OpenAI.Images;
namespace BotSharp.Plugin.AzureOpenAI.Providers;
@ -47,21 +34,23 @@ public class ImageGenerationProvider : IImageGeneration
}
var client = ProviderHelper.GetClient(Provider, _model, _services);
var options = PrepareOptions(conversations);
var response = await client.GetImageGenerationsAsync(options);
var image = response.Value.Data.First();
var (prompt, options) = PrepareOptions(conversations);
var imageClient = client.GetImageClient(_model);
var response = imageClient.GenerateImage(prompt, options);
var value = response.Value;
var content = string.Empty;
if (!string.IsNullOrEmpty(image.RevisedPrompt))
if (!string.IsNullOrEmpty(value.RevisedPrompt))
{
content = image.RevisedPrompt;
content = value.RevisedPrompt;
}
var responseMessage = new RoleDialogModel(AgentRole.Assistant, content)
{
CurrentAgentId = agent.Id,
MessageId = conversations.LastOrDefault()?.MessageId ?? string.Empty,
Data = image.Url.AbsoluteUri ?? image.Base64Data
Data = options.ResponseFormat == GeneratedImageFormat.Uri ? value.ImageUri?.AbsoluteUri : value.ImageBytes
};
// After
@ -69,10 +58,10 @@ public class ImageGenerationProvider : IImageGeneration
{
await hook.AfterGenerated(responseMessage, new TokenStatsModel
{
Prompt = options.Prompt,
Prompt = prompt,
Provider = Provider,
Model = _model,
PromptCount = options.Prompt.Split(' ', StringSplitOptions.RemoveEmptyEntries).Count(),
PromptCount = prompt.Split(' ', StringSplitOptions.RemoveEmptyEntries).Count(),
CompletionCount = content.Split(' ', StringSplitOptions.RemoveEmptyEntries).Count()
});
}
@ -80,25 +69,99 @@ public class ImageGenerationProvider : IImageGeneration
return responseMessage;
}
private ImageGenerationOptions PrepareOptions(List<RoleDialogModel> conversations)
private (string, ImageGenerationOptions) PrepareOptions(List<RoleDialogModel> conversations)
{
var state = _services.GetRequiredService<IConversationStateService>();
var prompt = conversations.LastOrDefault()?.Payload ?? conversations.LastOrDefault()?.Content ?? string.Empty;
var sizeValue = !string.IsNullOrEmpty(state.GetState("image_size")) ? state.GetState("image_size") : "1024x1024";
var qualityValue = !string.IsNullOrEmpty(state.GetState("image_quality")) ? state.GetState("image_quality") : "standard";
var state = _services.GetRequiredService<IConversationStateService>();
var size = state.GetState("image_size");
var quality = state.GetState("image_quality");
var style = state.GetState("image_style");
var options = new ImageGenerationOptions
{
DeploymentName = _model,
Prompt = conversations.LastOrDefault()?.Payload ?? conversations.LastOrDefault()?.Content ?? string.Empty,
Size = new ImageSize(sizeValue),
Quality = new ImageGenerationQuality(qualityValue)
Size = GetImageSize(size),
Quality = GetImageQuality(quality),
Style = GetImageStyle(style),
ResponseFormat = GeneratedImageFormat.Uri
};
return options;
return (prompt, options);
}
public void SetModelName(string model)
{
_model = model;
}
private GeneratedImageSize GetImageSize(string size)
{
var value = !string.IsNullOrEmpty(size) ? size : "1024x1024";
GeneratedImageSize retSize;
switch (value)
{
case "256x256":
retSize = GeneratedImageSize.W256xH256;
break;
case "512x512":
retSize = GeneratedImageSize.W512xH512;
break;
case "1024x1024":
retSize = GeneratedImageSize.W1024xH1024;
break;
case "1024x1792":
retSize = GeneratedImageSize.W1024xH1792;
break;
case "1792x1024":
retSize = GeneratedImageSize.W1792xH1024;
break;
default:
retSize = GeneratedImageSize.W1024xH1024;
break;
}
return retSize;
}
private GeneratedImageQuality GetImageQuality(string quality)
{
var value = !string.IsNullOrEmpty(quality) ? quality : "standard";
GeneratedImageQuality retQuality;
switch (value)
{
case "standard":
retQuality = GeneratedImageQuality.Standard;
break;
case "hd":
retQuality = GeneratedImageQuality.High;
break;
default:
retQuality = GeneratedImageQuality.Standard;
break;
}
return retQuality;
}
private GeneratedImageStyle GetImageStyle(string style)
{
var value = !string.IsNullOrEmpty(style) ? style : "natural";
GeneratedImageStyle retStyle;
switch (value)
{
case "standard":
retStyle = GeneratedImageStyle.Natural;
break;
case "vivid":
retStyle = GeneratedImageStyle.Vivid;
break;
default:
retStyle = GeneratedImageStyle.Natural;
break;
}
return retStyle;
}
}

View file

@ -1,7 +1,3 @@
using BotSharp.Plugin.AzureOpenAI.Settings;
using Microsoft.Extensions.Logging;
using System;
namespace BotSharp.Plugin.AzureOpenAI.Providers;
public class OpenAiChatCompletionProvider : ChatCompletionProvider

View file

@ -1,7 +1,3 @@
using BotSharp.Plugin.AzureOpenAI.Settings;
using Microsoft.Extensions.Logging;
using System;
namespace BotSharp.Plugin.AzureOpenAI.Providers;
public class OpenAiImageGenerationProvider : ImageGenerationProvider

View file

@ -1,10 +1,7 @@
using Azure.AI.OpenAI;
using Azure;
using System;
using BotSharp.Abstraction.Conversations.Models;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using BotSharp.Abstraction.MLTasks;
using OpenAI;
using System.ClientModel;
namespace BotSharp.Plugin.AzureOpenAI.Providers;
@ -15,8 +12,8 @@ public class ProviderHelper
var settingsService = services.GetRequiredService<ILlmProviderService>();
var settings = settingsService.GetSetting(provider, model);
var client = provider == "openai" ?
new OpenAIClient($"{settings.ApiKey}") :
new OpenAIClient(new Uri(settings.Endpoint), new AzureKeyCredential(settings.ApiKey));
new OpenAIClient(new ApiKeyCredential(settings.ApiKey)) :
new AzureOpenAIClient(new Uri(settings.Endpoint), new AzureKeyCredential(settings.ApiKey));
return client;
}

View file

@ -1,16 +1,4 @@
using Azure.AI.OpenAI;
using BotSharp.Abstraction.MLTasks;
using System;
using System.Threading.Tasks;
using BotSharp.Plugin.AzureOpenAI.Settings;
using BotSharp.Abstraction.Conversations;
using Microsoft.Extensions.DependencyInjection;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Agents.Enums;
using System.Linq;
using System.Collections.Generic;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Loggers;
using OpenAI.Chat;
namespace BotSharp.Plugin.AzureOpenAI.Providers;
@ -51,30 +39,28 @@ public class TextCompletionProvider : ITextCompletion
})).ToArray());
var client = ProviderHelper.GetClient(Provider, _model, _services);
var chatClient = client.GetChatClient(_model);
var completionsOptions = new CompletionsOptions()
var messages = new List<ChatMessage>()
{
Prompts =
{
text
},
MaxTokens = 256,
new UserChatMessage(text)
};
completionsOptions.StopSequences.Add($"{AgentRole.Assistant}:");
var state = _services.GetRequiredService<IConversationStateService>();
var temperature = float.Parse(state.GetState("temperature", "0.0"));
var samplingFactor = float.Parse(state.GetState("sampling_factor", "0.0"));
completionsOptions.Temperature = temperature;
completionsOptions.NucleusSamplingFactor = samplingFactor;
completionsOptions.DeploymentName = _model;
var response = await client.GetCompletionsAsync(completionsOptions);
var completionOptions = new ChatCompletionOptions()
{
MaxTokens = 256,
Temperature = temperature
};
var response = await chatClient.CompleteChatAsync(messages, completionOptions);
// OpenAI
var completion = "";
foreach (var t in response.Value.Choices)
foreach (var t in response.Value.Content)
{
completion += t.Text;
completion += t?.Text ?? string.Empty;
};
// After chat completion hook
@ -89,8 +75,8 @@ public class TextCompletionProvider : ITextCompletion
Prompt = text,
Provider = Provider,
Model = _model,
PromptCount = response.Value.Usage.PromptTokens,
CompletionCount = response.Value.Usage.CompletionTokens
PromptCount = response.Value.Usage.InputTokens,
CompletionCount = response.Value.Usage.OutputTokens
})).ToArray());
return completion.Trim();

View file

@ -0,0 +1,17 @@
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.IO;
global using System.Threading.Tasks;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Logging;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Loggers;
global using BotSharp.Abstraction.MLTasks;
global using BotSharp.Abstraction.Agents;
global using BotSharp.Abstraction.Files;
global using BotSharp.Abstraction.Utilities;
global using BotSharp.Plugin.AzureOpenAI.Settings;

View file

@ -11,6 +11,8 @@
</PropertyGroup>
<ItemGroup>
<None Remove="data\agents\00000000-0000-0000-0000-000000000000\functions\handle_http_request.json" />
<None Remove="data\agents\00000000-0000-0000-0000-000000000000\templates\handle_http_request.fn.liquid" />
<None Remove="data\agents\87c458fc-ec5f-40ae-8ed6-05dda8a07523\agent.json" />
<None Remove="data\agents\87c458fc-ec5f-40ae-8ed6-05dda8a07523\functions.json" />
<None Remove="data\agents\87c458fc-ec5f-40ae-8ed6-05dda8a07523\instruction.liquid" />
@ -26,6 +28,12 @@
<Content Include="data\agents\87c458fc-ec5f-40ae-8ed6-05dda8a07523\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\00000000-0000-0000-0000-000000000000\functions\handle_http_request.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\00000000-0000-0000-0000-000000000000\templates\handle_http_request.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1,6 @@
namespace BotSharp.Plugin.HttpHandler.Enums;
public class Tool
{
public const string HttpHandler = "http-handler";
}

View file

@ -0,0 +1,59 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Agents.Settings;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Plugin.HttpHandler.Enums;
namespace BotSharp.Plugin.HttpHandler.Hooks;
public class HttpHandlerHook : AgentHookBase
{
private static string TOOL_ASSISTANT = Guid.Empty.ToString();
public override string SelfId => string.Empty;
public HttpHandlerHook(IServiceProvider services, AgentSettings settings)
: base(services, settings)
{
}
public override void OnAgentLoaded(Agent agent)
{
var conv = _services.GetRequiredService<IConversationService>();
var isConvMode = conv.IsConversationMode();
var isEnabled = !agent.Tools.IsNullOrEmpty() && agent.Tools.Contains(Tool.HttpHandler);
if (isConvMode && isEnabled)
{
var (prompt, fn) = GetPromptAndFunction();
if (fn != null)
{
if (!string.IsNullOrWhiteSpace(prompt))
{
agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n";
}
if (agent.Functions == null)
{
agent.Functions = new List<FunctionDef> { fn };
}
else
{
agent.Functions.Add(fn);
}
}
}
base.OnAgentLoaded(agent);
}
private (string, FunctionDef?) GetPromptAndFunction()
{
var fn = "handle_http_request";
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.GetAgent(TOOL_ASSISTANT);
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{fn}.fn"))?.Content ?? string.Empty;
var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(fn));
return (prompt, loadAttachmentFn);
}
}

View file

@ -0,0 +1,12 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Plugin.HttpHandler.Enums;
namespace BotSharp.Plugin.HttpHandler.Hooks;
public class HttpHandlerToolHook : IAgentToolHook
{
public void AddTools(List<string> tools)
{
tools.Add(Tool.HttpHandler);
}
}

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Http.Settings;
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Settings;
using BotSharp.Plugin.HttpHandler.Hooks;
using Microsoft.Extensions.Configuration;
namespace BotSharp.Plugin.HttpHandler;
@ -19,5 +20,8 @@ public class HttpHandlerPlugin : IBotSharpPlugin
var settingService = provider.GetRequiredService<ISettingService>();
return settingService.Bind<HttpSettings>("Http");
});
services.AddScoped<IAgentHook, HttpHandlerHook>();
services.AddScoped<IAgentToolHook, HttpHandlerToolHook>();
}
}

View file

@ -0,0 +1,22 @@
{
"name": "handle_http_request",
"description": "If the user requests to send an http request, you need to capture the http method and request content, and then call this function to send the http request.",
"parameters": {
"type": "object",
"properties": {
"request_url": {
"type": "string",
"description": "The http url that is requested. It can be an absolute url that starts with 'http' or 'https', or a relative url that starts with '/'"
},
"http_method": {
"type": "string",
"description": "The http method that is requested, e.g., GET, POST, PUT, and DELETE."
},
"request_content": {
"type": "string",
"description": "The http request content. It must be in json format.."
}
},
"required": [ "request_url", "http_method" ]
}
}

View file

@ -0,0 +1 @@
Please call handle_http_request if user wants to send an http request.

View file

@ -13,6 +13,10 @@
<ItemGroup>
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\get_table_columns.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\lookup_dictionary.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_insert.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_select.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\instruction.liquid" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\lookup_dictionary.liquid" />
</ItemGroup>
@ -21,15 +25,27 @@
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\lookup_dictionary.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\get_table_columns.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\lookup_dictionary.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_insert.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_select.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1,14 @@
{
"name": "get_table_columns",
"description": "Get related table columns and foreign key informations",
"parameters": {
"type": "object",
"properties": {
"table": {
"type": "string",
"description": "table name"
}
},
"required": [ "table" ]
}
}

View file

@ -0,0 +1,30 @@
{
"name": "lookup_dictionary",
"description": "Get id from dictionary table by keyword if tool or solution mentioned this approach",
"parameters": {
"type": "object",
"properties": {
"table": {
"type": "string",
"description": "table name"
},
"keyword": {
"type": "string",
"description": "table name"
},
"reason": {
"type": "string",
"description": "the reason why you need to call lookup_dictionary"
},
"columns": {
"type": "array",
"description": "columns",
"items": {
"type": "string",
"description": "column"
}
}
},
"required": [ "table", "keyword", "reason", "columns" ]
}
}

View file

@ -0,0 +1,56 @@
{
"name": "sql_insert",
"description": "Insert query is generated if the record doesn't exist.",
"parameters": {
"type": "object",
"properties": {
"sql_statement": {
"type": "string",
"description": "INSERT SQL statement. The value should use the parameter name like @field."
},
"reason": {
"type": "string",
"description": "reason"
},
"table": {
"type": "string",
"description": "related table"
},
"parameters": {
"type": "array",
"description": "a list of parameters in the statement match with the variables",
"items": {
"type": "object",
"description": "{name:'', value:''}",
"properties": {
"name": {
"type": "string",
"description": "field name"
},
"value": {
"type": "string",
"description": "real value inferred by the context"
}
},
"required": [ "name", "value" ]
}
},
"return_field": {
"type": "object",
"description": "the name and alias for the return field",
"properties": {
"name": {
"type": "string",
"description": "field name"
},
"alias": {
"type": "string",
"description": "meaningful field alias"
}
},
"required": [ "name", "alias" ]
}
},
"required": [ "sql_statement", "reason", "table", "parameters", "return_field" ]
}
}

View file

@ -0,0 +1,56 @@
{
"name": "sql_select",
"description": "Get the specific value from table",
"parameters": {
"type": "object",
"properties": {
"sql_statement": {
"type": "string",
"description": "SQL statement with SELECT"
},
"reason": {
"type": "string",
"description": "reason"
},
"table": {
"type": "string",
"description": "related table"
},
"parameters": {
"type": "array",
"description": "data criteria for the query",
"items": {
"type": "object",
"description": "the name and value for the parameter",
"properties": {
"name": {
"type": "string",
"description": "field name"
},
"value": {
"type": "string",
"description": "real value inferred by the context"
}
},
"required": [ "name", "value" ]
}
},
"return_field": {
"type": "object",
"description": "the name and alias for the return field",
"properties": {
"name": {
"type": "string",
"description": "field in the table"
},
"alias": {
"type": "string",
"description": "meaningful field alias"
}
},
"required": [ "name", "value" ]
}
},
"required": [ "sql_statement", "reason", "table", "parameters", "return_field" ]
}
}

View file

@ -25,6 +25,20 @@
<ItemGroup>
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\agent.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\change_checkbox.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\change_list_value.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\check_radio_button.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\click_button.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\click_element.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\close_browser.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\extract_data_from_page.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\go_to_page.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\input_user_password.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\input_user_text.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\open_browser.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\scroll_page.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\send_http_request.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\take_screenshot.json" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\instruction.liquid" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\templates\extract_data.liquid" />
<None Remove="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\templates\html_parser.liquid" />
@ -47,6 +61,48 @@
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\templates\html_parser.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\change_checkbox.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\change_list_value.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\check_radio_button.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\click_button.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\click_element.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\close_browser.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\extract_data_from_page.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\go_to_page.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\input_user_password.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\input_user_text.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\open_browser.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\scroll_page.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\send_http_request.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b\functions\take_screenshot.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1,22 @@
{
"name": "change_checkbox",
"description": "Check or uncheck checkbox",
"parameters": {
"type": "object",
"properties": {
"element_text": {
"type": "string",
"description": "the element title"
},
"update_value": {
"type": "string",
"description": "check or uncheck"
},
"match_rule": {
"type": "string",
"description": "text matching rule: EndWith, StartWith, Contains, Match"
}
},
"required": [ "element_text", "update_value", "match_rule" ]
}
}

View file

@ -0,0 +1,18 @@
{
"name": "change_list_value",
"description": "Update value from dropdown list or radio button",
"parameters": {
"type": "object",
"properties": {
"element_name": {
"type": "string",
"description": "the html selection element name."
},
"update_value": {
"type": "string",
"description": "the value in the list."
}
},
"required": [ "element_name", "update_value" ]
}
}

View file

@ -0,0 +1,22 @@
{
"name": "check_radio_button",
"description": "Check value in a radio button",
"parameters": {
"type": "object",
"properties": {
"element_text": {
"type": "string",
"description": "the element title"
},
"update_value": {
"type": "string",
"description": "the value in the radio button."
},
"match_rule": {
"type": "string",
"description": "text matching rule: EndWith, StartWith, Contains, Match"
}
},
"required": [ "update_value", "element_text", "match_rule" ]
}
}

View file

@ -0,0 +1,14 @@
{
"name": "click_button",
"description": "Click a button in a web page.",
"parameters": {
"type": "object",
"properties": {
"element_name": {
"type": "string",
"description": "the html element name."
}
},
"required": [ "element_name" ]
}
}

View file

@ -0,0 +1,30 @@
{
"name": "click_element",
"description": "Click or check an element contains some text",
"parameters": {
"type": "object",
"properties": {
"element_type": {
"type": "string",
"description": "the element tag name"
},
"element_text": {
"type": "string",
"description": "text or placeholder shown in the element."
},
"attribute_name": {
"type": "string",
"description": "attribute name in the element"
},
"attribute_value": {
"type": "string",
"description": "attribute value in the element"
},
"match_rule": {
"type": "string",
"description": "text matching rule: EndWith, StartWith, Contains, Match"
}
},
"required": [ "element_type", "element_text", "match_rule" ]
}
}

View file

@ -0,0 +1,10 @@
{
"name": "close_browser",
"description": "Close browser",
"parameters": {
"type": "object",
"properties": {
},
"required": []
}
}

View file

@ -0,0 +1,14 @@
{
"name": "extract_data_from_page",
"description": "Extract data from current web page.",
"parameters": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "the information user wants to know"
}
},
"required": [ "question" ]
}
}

View file

@ -0,0 +1,14 @@
{
"name": "go_to_page",
"description": "go to another page",
"parameters": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "page url start with https://"
}
},
"required": [ "url" ]
}
}

View file

@ -0,0 +1,14 @@
{
"name": "input_user_password",
"description": "Input password in current web page",
"parameters": {
"type": "object",
"properties": {
"password": {
"type": "string",
"description": "user password"
}
},
"required": [ "password" ]
}
}

View file

@ -0,0 +1,30 @@
{
"name": "input_user_text",
"description": "Input non-sensitive text in current web page.",
"parameters": {
"type": "object",
"properties": {
"element_text": {
"type": "string",
"description": "text or placeholder shown in the element."
},
"input_text": {
"type": "string",
"description": "non-sensitive text user provided."
},
"press_enter": {
"type": "boolean",
"description": "whether to press Enter key"
},
"attribute_name": {
"type": "string",
"description": "attribute name in the element"
},
"attribute_value": {
"type": "string",
"description": "attribute value in the element"
}
},
"required": [ "element_text", "input_text" ]
}
}

View file

@ -0,0 +1,14 @@
{
"name": "open_browser",
"description": "open a browser",
"parameters": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "website url starts with https://"
}
},
"required": [ "url" ]
}
}

View file

@ -0,0 +1,14 @@
{
"name": "scroll_page",
"description": "Scroll page down or up",
"parameters": {
"type": "object",
"properties": {
"direction": {
"type": "string",
"description": "down, up, left, right"
}
},
"required": [ "direction" ]
}
}

View file

@ -0,0 +1,18 @@
{
"name": "send_http_request",
"description": "Send http request to remote server",
"parameters": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "page url start with https://"
},
"payload": {
"type": "string",
"description": "request body"
}
},
"required": [ "url", "payload" ]
}
}

View file

@ -0,0 +1,10 @@
{
"name": "take_screenshot",
"description": "Tak screenshot to show current page screen",
"parameters": {
"type": "object",
"properties": {
},
"required": []
}
}

View file

@ -292,7 +292,8 @@
"BotSharp.Plugin.WebDriver",
"BotSharp.Plugin.LLamaSharp",
"BotSharp.Plugin.SparkDesk",
"BotSharp.Plugin.MetaGLM"
"BotSharp.Plugin.MetaGLM",
"BotSharp.Plugin.HttpHandler"
]
}
}

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
@ -24,13 +24,18 @@
<None Remove="data\agents\8970b1e5-d260-4e2c-90b1-f1415a257c18\templates\task.place_pizza_order.liquid" />
<None Remove="data\agents\b284db86-e9c2-4c25-a59e-4649797dd130\agent.json" />
<None Remove="data\agents\b284db86-e9c2-4c25-a59e-4649797dd130\functions.json" />
<None Remove="data\agents\b284db86-e9c2-4c25-a59e-4649797dd130\functions\get_order_status.json" />
<None Remove="data\agents\b284db86-e9c2-4c25-a59e-4649797dd130\instruction.liquid" />
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\agent.json" />
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\functions.json" />
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\functions\get_pizza_price.json" />
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\functions\get_pizza_types.json" />
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\functions\place_an_order.json" />
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\instruction.liquid" />
<None Remove="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\responses\func.get_pizza_price.0.liquid" />
<None Remove="data\agents\fe8c60aa-b114-4ef3-93cb-a8efeac80f75\agent.json" />
<None Remove="data\agents\fe8c60aa-b114-4ef3-93cb-a8efeac80f75\functions.json" />
<None Remove="data\agents\fe8c60aa-b114-4ef3-93cb-a8efeac80f75\functions\make_payment.json" />
<None Remove="data\agents\fe8c60aa-b114-4ef3-93cb-a8efeac80f75\instruction.liquid" />
<None Remove="data\users\10d12798-08fb-4aa6-977b-5dd94d82dbfe\user.json" />
<None Remove="data\users\456e35c5-caf0-4d45-9084-b44a8ca717e4\user.json" />
@ -87,6 +92,21 @@
<Content Include="data\users\e465af5f-044f-414b-b670-92834929b96c\user.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\b284db86-e9c2-4c25-a59e-4649797dd130\functions\get_order_status.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\functions\get_pizza_price.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\functions\get_pizza_types.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd\functions\place_an_order.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\fe8c60aa-b114-4ef3-93cb-a8efeac80f75\functions\make_payment.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="$(SolutionName)==BotSharp">

View file

@ -0,0 +1,14 @@
{
"name": "get_order_status",
"description": "get order status like delivery remaining time",
"parameters": {
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "order number."
}
},
"required": [ "order_number" ]
}
}

View file

@ -0,0 +1,18 @@
{
"name": "get_pizza_price",
"description": "call this function to get the pizza price",
"parameters": {
"type": "object",
"properties": {
"pizza_type": {
"type": "string",
"description": "The pizza type."
},
"quantity": {
"type": "string",
"description": "quantity of pizza."
}
},
"required": [ "pizza_type", "quantity" ]
}
}

View file

@ -0,0 +1,9 @@
{
"name": "get_pizza_types",
"description": "get all pizza types",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}

View file

@ -0,0 +1,22 @@
{
"name": "place_an_order",
"description": "Place an order when user has confirmed the pizza type and quantity.",
"parameters": {
"type": "object",
"properties": {
"pizza_type": {
"type": "string",
"description": "The pizza type."
},
"quantity": {
"type": "number",
"description": "quantity of pizza."
},
"unit_price": {
"type": "number",
"description": "unit price"
}
},
"required": [ "pizza_type", "quantity", "unit_price" ]
}
}

View file

@ -0,0 +1,18 @@
{
"name": "make_payment",
"description": "call this function to make payment",
"parameters": {
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "order number."
},
"total_amount": {
"type": "string",
"description": "total amount."
}
},
"required": [ "order_number", "total_amount" ]
}
}