Merge branch 'SciSharp:master' into master
This commit is contained in:
commit
c6f64a605a
|
|
@ -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; }
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
Loading…
Reference in a new issue