ChatApi/Services/Bot/Structs/ChatBot.cs
2024-12-22 15:49:43 -03:00

219 lines
8.3 KiB
C#

using Amazon.Auth.AccessControlPolicy;
using ChatApi.Models;
using Microsoft.SemanticKernel.ChatCompletion;
using System.Text;
namespace ChatApi.Services.Bot.Structs
{
public class ChatBot
{
private readonly List<Question> _questions;
private readonly ChatPersistence _persistence;
private readonly Dictionary<int, string> _answers;
private readonly IChatCompletionService _chatCompletionService;
private readonly IActionCall _actionCall;
private readonly string _usuarioId;
private readonly UserData _userData;
private Dictionary<string, string> _knowParameters;
private int _indiceAtual;
public ChatBot(ChatPersistence persistence, string usuarioId, UserData userData, IChatCompletionService chatCompletionService, IActionCall actionCall)
{
_chatCompletionService = chatCompletionService;
_actionCall = actionCall;
_questions = new List<Question>();
_answers = new Dictionary<int, string>();
_knowParameters = new Dictionary<string, string>();
_indiceAtual = 0;
_persistence = persistence;
_usuarioId = usuarioId;
_userData = userData;
var estado = _persistence.GetState(_usuarioId);
if (estado != null)
{
_indiceAtual = estado.IndicePerguntaAtual;
_answers = estado.Respostas;
}
else
{
_indiceAtual = 0;
}
}
public void SetParameter(string key, string value)
{
_knowParameters[key] = value;
}
public void AddQuestion(Question Question)
{
_questions.Add(Question);
}
public string SetAnswer(string resposta, bool needsRestart = false)
{
var stops = new List<string>()
{
"ABORTAR",
"ABORTE",
"SAIR",
"SAIA",
"REINICIAR",
"REINICIE"
};
if (!this.HasNextQuestion()) {
SetQuestionIndex(_usuarioId, true);
return "";
}
var state = _persistence.GetState(_usuarioId);
SetQuestionIndex(_usuarioId, needsRestart);
if (state != null)
{
_questions[_indiceAtual].CountTry = state.TentativasPerguntaAtual;
}
else
{
_knowParameters = new Dictionary<string, string>()
{
{ "Nome", _userData.Name }
};
}
//var testAll = TestAllQuestions(resposta);
//if (int.TryParse(testAll, out int index))
// _indiceAtual = index < 0 ? _indiceAtual : index - 1;
//var abort = resposta.ToUpper();
//if (index < 0) abort= "ABORTAR";
//var perguntaAtual = _questions[_indiceAtual];
//var testByChat = TestAnswerByChat(resposta);
var perguntaAtual = _questions[_indiceAtual];
var abort = resposta.ToUpper();
var validResp = perguntaAtual.TryToAnswer(resposta);
//if (string.IsNullOrEmpty(validResp) && !abort.Contains("ABORTAR") && perguntaAtual.Validator(resposta) && testByChat.Contains("SIM"))
if (string.IsNullOrEmpty(validResp) && !stops.Contains(abort))
{
_answers[_indiceAtual] = perguntaAtual.ConvertToSave == null ? resposta : perguntaAtual.ConvertToSave(resposta);
_indiceAtual++;
if (_indiceAtual < _questions.Count())
{
_persistence.SaveState(new ChatState
{
Id = _usuarioId,
UsuarioId = _usuarioId,
IndicePerguntaAtual = _indiceAtual,
TentativasPerguntaAtual = _questions[_indiceAtual].CountTry,
Respostas = new Dictionary<int, string>(_answers)
});
}
return "";
}
else if (validResp.Contains("reiniciar") || abort.Contains("ABORTAR"))
{
_persistence.DeleteState(_usuarioId);
return "REINICIAR";
}
_persistence.SaveState(new ChatState
{
Id = _usuarioId,
UsuarioId = _usuarioId,
IndicePerguntaAtual = _indiceAtual,
TentativasPerguntaAtual = _questions[_indiceAtual].CountTry,
DadosConhecidos = _knowParameters,
Respostas = new Dictionary<int, string>(_answers)
});
//return $"Resposta inválida. Tente novamente. {(testByChat.Contains("NÃO") ? "Motivo: " + testByChat.Replace("NÂO", "") : "")} \n\n {perguntaAtual.Text}";
return $"Resposta inválida. Tente novamente. \n{perguntaAtual.Text} \nSua resposta: {resposta}";
}
public string GetCurrentQuestion() => _questions[_indiceAtual].Text;
public async Task<string> GetNextQuestion()
{
while (_indiceAtual < _questions.Count)
{
var perguntaAtual = _questions[_indiceAtual];
if (perguntaAtual.MustShow(_answers))
{
return perguntaAtual.Text;
}
_indiceAtual++;
}
return null;
}
public async Task<string?> CallFinalAction()
{
_knowParameters = new Dictionary<string, string>()
{
{ "Nome", _userData.Name }
};
var resp = await _actionCall.Populate(_knowParameters, _answers);
if (!resp.Success) return "Obrigado por responder a nossas perguntas!";
var result = await _actionCall.Call();
return result.Success ? result.Value : "Obrigado por responder a nossas perguntas!";
}
public string TestAnswerByChat(string resposta)
{
var resp = _chatCompletionService.GetChatMessageContentAsync($"Por favor, responda com SIM/NÂO e o motivo para eu saber se a pergunta: ({this.GetCurrentQuestion()}) foi respondida pelo usuário quando ele digitou: ({resposta})? É um chatbot, por isso preciso saber apenas se a resposta faz sentido e/ou tem o formato certo.").Result;
return resp.Content;
}
public string TestIfWantToAbort(string resposta)
{
if (_indiceAtual == 0) return "";
var resp = _chatCompletionService.GetChatMessageContentAsync($"Este é um chatbot. Foi feita a pergunta para o usuario: ({this.GetCurrentQuestion()}) e ele digitou: ({resposta})? Ele respondeu a pergunta com a informação solicitada, ou ele está pedindo para sair do chat? Responda com (SIM = Ele quer sair ou não quer responder) ou (NÃO = Ele responde a pergunta de maneira coerente) e indique o motivo.").Result;
return resp.Content.Contains("SIM") ? "ABORTAR" : "";
}
public string? TestAllQuestions(string resposta)
{
if (_indiceAtual == 0) return "";
var builder = new StringBuilder();
for (int i = 0; i < _questions.Count; i++)
{
builder.AppendLine($"{i + 1} - {_questions[i].Text}");
}
var resp = _chatCompletionService.GetChatMessageContentAsync($"Este é um chatbot. Você pode identificar se a resposta do usuario: \"{resposta}\" tem a inteção de responder uma das perguntas: { builder.ToString() } ? Por favor, reponda com o número da pergunta que o usuário tentou responder, ou com a palavra -1 se não identificou, ou por ultimo com -2 se identificar que o usuário não quis responder nenhuma das perguntas.").Result;
return resp.Content;
}
public bool HasNextQuestion()
{
return _indiceAtual < _questions.Count;
}
private void SetQuestionIndex(string usuarioId, bool needsRestart)
{
var estado = _persistence.GetState(usuarioId);
if (estado != null && !needsRestart)
{
_indiceAtual = estado.IndicePerguntaAtual;
}
else
{
_indiceAtual = 0;
}
}
}
}