Merge branch 'SciSharp:master' into master

This commit is contained in:
hchen2020 2023-12-01 11:32:20 -06:00 committed by GitHub
commit c6f64a605a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 11 deletions

View file

@ -0,0 +1,21 @@
using System.Text.Json;
namespace BotSharp.Abstraction.Functions.Models;
/// <summary>
/// This class defines the LLM response output if function call needed
/// </summary>
public class FunctionCallingResponse
{
[JsonPropertyName("role")]
public string Role { get; set; } = AgentRole.Assistant;
[JsonPropertyName("content")]
public string? Content { get; set; }
[JsonPropertyName("function_name")]
public string? FunctionName { get; set; }
[JsonPropertyName("args")]
public JsonDocument? Args { get; set; }
}

View file

@ -4,7 +4,10 @@ public class FunctionDef
{
public string Name { get; set; }
public string Description { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Impact { get; set; }
public FunctionParametersDef Parameters { get; set; } = new FunctionParametersDef();
public override string ToString()

View file

@ -1,10 +1,12 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing;
using BotSharp.Plugin.GoogleAI.Settings;
using LLMSharp.Google.Palm;
using Microsoft.Extensions.Logging;
using LLMSharp.Google.Palm.DiscussService;
namespace BotSharp.Plugin.GoogleAI.Providers;
@ -34,29 +36,105 @@ public class ChatCompletionProvider : IChatCompletion
hook.BeforeGenerating(agent, conversations)).ToArray());
var client = new GooglePalmClient(apiKey: _settings.PaLM.ApiKey);
var messages = conversations.Select(c => new PalmChatMessage(c.Content, c.Role == AgentRole.User ? "user" : "AI"))
.ToList();
var agentService = _services.GetRequiredService<IAgentService>();
var instruction = agentService.RenderedInstruction(agent);
var response = client.ChatAsync(messages, instruction, null).Result;
var (prompt, messages, hasFunctions) = PrepareOptions(agent, conversations);
var message = response.Candidates.First();
var msg = new RoleDialogModel(AgentRole.Assistant, message.Content)
RoleDialogModel msg;
if (hasFunctions)
{
CurrentAgentId = agent.Id
};
// use text completion
// var response = client.GenerateTextAsync(prompt, null).Result;
var response = client.ChatAsync(new PalmChatCompletionRequest
{
Context = prompt,
Messages = messages,
Temperature = 0.1f
}).Result;
var message = response.Candidates.First();
// check if returns function calling
var llmResponse = message.Content.JsonContent<FunctionCallingResponse>();
msg = new RoleDialogModel(llmResponse.Role, llmResponse.Content)
{
CurrentAgentId = agent.Id,
FunctionName = llmResponse.FunctionName,
FunctionArgs = JsonSerializer.Serialize(llmResponse.Args)
};
}
else
{
var response = client.ChatAsync(messages, context: prompt, examples: null, options: null).Result;
var message = response.Candidates.First();
// check if returns function calling
var llmResponse = message.Content.JsonContent<FunctionCallingResponse>();
msg = new RoleDialogModel(llmResponse.Role, llmResponse.Content ?? message.Content)
{
CurrentAgentId = agent.Id
};
}
// After chat completion hook
Task.WaitAll(hooks.Select(hook =>
hook.AfterGenerated(msg, new TokenStatsModel
{
Prompt = prompt,
Model = _model
})).ToArray());
return msg;
}
private (string, List<PalmChatMessage>, bool) PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
{
var prompt = "";
var agentService = _services.GetRequiredService<IAgentService>();
if (!string.IsNullOrEmpty(agent.Instruction))
{
prompt += agentService.RenderedInstruction(agent);
}
var routing = _services.GetRequiredService<IRoutingService>();
var router = routing.Router;
var messages = conversations.Select(c => new PalmChatMessage(c.Content, c.Role == AgentRole.User ? "user" : "AI"))
.ToList();
if (agent.Functions != null && agent.Functions.Count > 0)
{
prompt += "\r\n\r\n[Functions] defined in JSON Schema:\r\n";
prompt += JsonSerializer.Serialize(agent.Functions, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
});
prompt += "\r\n\r\n[Conversations]\r\n";
foreach (var dialog in conversations)
{
prompt += dialog.Role == AgentRole.Function ?
$"{dialog.Role}: {dialog.FunctionName} => {dialog.Content}\r\n" :
$"{dialog.Role}: {dialog.Content}\r\n";
}
prompt += "\r\n\r\n" + router.Templates.FirstOrDefault(x => x.Name == "response_with_function").Content;
return (prompt, new List<PalmChatMessage>
{
new PalmChatMessage("Which function should be used for the next step based on latest user or function response, output your response in JSON:", AgentRole.User),
}, true);
}
return (prompt, messages, false);
}
public Task<bool> GetChatCompletionsAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived, Func<RoleDialogModel, Task> onFunctionExecuting)
{
throw new NotImplementedException();

View file

@ -1,4 +1,4 @@
What is the next step based on the CONVERSATION?
Response must be in appropriate JSON format.
Response must be in required JSON format without any other contents.
Route to the Agent that last handled the conversation if necessary.
If user wants to speak to customer service, use function human_intervention_needed.

View file

@ -0,0 +1,9 @@
[Output Requirements]
1. Read the [Functions] definition, you can utilize the function to retrieve data or execute actions.
2. Think step by step, check if specific function will provider data to help complete user request based on the conversation.
3. If you need to call a function to decide how to response user,
response in format: {"role": "function", "reason":"why choose this function", "function_name": "", "args": {}},
otherwise response in format: {"role": "assistant", "reason":"why response to user", "content":"next step question"}.
4. If the conversation already contains the function execution result, don't need to call it again.
5. If user mentioned some specific requirment, don't ask this question in your response.
6. Don't repeat the same question in your response.