Add project files.
This commit is contained in:
parent
5755573891
commit
9ae0748ec6
30
.dockerignore
Normal file
30
.dockerignore
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
!**/.gitignore
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/config
|
||||||
|
!.git/packed-refs
|
||||||
|
!.git/refs/heads/**
|
||||||
4
.editorconfig
Normal file
4
.editorconfig
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# SKEXP0070: Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
|
dotnet_diagnostic.SKEXP0070.severity = silent
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
################################################################################
|
||||||
|
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
/.vs
|
||||||
|
/bin/Debug/net8.0
|
||||||
|
/obj
|
||||||
11
AuthMiddleware.cs
Normal file
11
AuthMiddleware.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
|
||||||
|
namespace ChatApi
|
||||||
|
{
|
||||||
|
public class AuthMiddleware
|
||||||
|
{
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
ChatApi.http
Normal file
6
ChatApi.http
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
@ChatApi_HostAddress = http://localhost:5020
|
||||||
|
|
||||||
|
GET {{ChatApi_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
94
ChatHistoryService.cs
Normal file
94
ChatHistoryService.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
using Microsoft.SemanticKernel;
|
||||||
|
using Microsoft.SemanticKernel.ChatCompletion;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ChatApi
|
||||||
|
{
|
||||||
|
public class ChatHistoryService
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, ChatHistory> _keyValues = new Dictionary<string, ChatHistory>();
|
||||||
|
public ChatHistoryService()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatHistory Get(string sessionId)
|
||||||
|
{
|
||||||
|
//var msg = new List<ChatMessageContent>();
|
||||||
|
////msg.Add(new ChatMessageContent(AuthorRole.System, "Your name is SuperChat. \nYou only generate answers using portuguese.\nYou are friendly and polite.\nYou speak only Brazilian Portuguese.\nYou never create a response in English. Always brazilian portuguese."));
|
||||||
|
//msg.Add(new ChatMessageContent(AuthorRole.System, "Seu nome é SuperChat."));
|
||||||
|
//msg.Add(new ChatMessageContent(AuthorRole.System, "Você só gera respostas usando português do Brasil."));
|
||||||
|
//msg.Add(new ChatMessageContent(AuthorRole.System, "Você fala apenas português brasileiro."));
|
||||||
|
//msg.Add(new ChatMessageContent(AuthorRole.System, "Você nunca cria respostas em inglês, mas sempre em português brasileiro."));
|
||||||
|
//string json = JsonSerializer.Serialize(msg);
|
||||||
|
//var history = new ChatHistory(JsonSerializer.Deserialize<List<ChatMessageContent>>(json));
|
||||||
|
|
||||||
|
//return history;
|
||||||
|
|
||||||
|
if (_keyValues.ContainsKey(sessionId))
|
||||||
|
{
|
||||||
|
var history = _keyValues[sessionId];
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var msg = new List<ChatMessageContent>();
|
||||||
|
TestePrompt(msg);
|
||||||
|
string json = JsonSerializer.Serialize(msg);
|
||||||
|
var history = new ChatHistory(JsonSerializer.Deserialize<List<ChatMessageContent>>(json));
|
||||||
|
_keyValues[sessionId] = history;
|
||||||
|
return _keyValues[sessionId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatHistory GetSumarizer(string sessionId)
|
||||||
|
{
|
||||||
|
if (_keyValues.ContainsKey(sessionId))
|
||||||
|
{
|
||||||
|
var history = _keyValues[sessionId];
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var msg = new List<ChatMessageContent>();
|
||||||
|
PromptLiliana(msg);
|
||||||
|
string json = JsonSerializer.Serialize(msg);
|
||||||
|
var history = new ChatHistory(JsonSerializer.Deserialize<List<ChatMessageContent>>(json));
|
||||||
|
_keyValues[sessionId] = history;
|
||||||
|
return _keyValues[sessionId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateHistory(string sessionId, ChatHistory history)
|
||||||
|
{
|
||||||
|
_keyValues[sessionId] = history;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TestePromptBot(List<ChatMessageContent> msg)
|
||||||
|
{
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Seu nome é Rosa, uma vendedora de bolos que está atendendo seus clientes. "));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Você tem apenas os seguintes sabores de bolo: chocolate, baunilha e morango. "));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Excepcionalmente hoje, o sabor morango está fora de estoque. Você não tem mais morangos. "));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Cada fatia de bolo custa 5 reais. \n"));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.User, "Responda sempre em portugues do Brasil as minhas perguntas."));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PromptLiliana(List<ChatMessageContent> msg)
|
||||||
|
{
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Seu nome é LiliAna, um assistente virtual da Domvs It (Consultoria e RH)."));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Você responde sempre em português do Brasil e fala sobre serviços prestados pela Domvs iT."));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Você consegue fazer solicitações no portal do RH da Domvs iT."));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Você responde perguntas voltadas ao mercado de trabalho de tecnologia."));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Em breve, você será capaz de consultar o linkedin."));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.User, "Use sempre portugues do Brasil."));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TestePrompt(List<ChatMessageContent> msg)
|
||||||
|
{
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Seu nome é Ian."));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Você só gera respostas usando português do Brasil."));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Você fala apenas português brasileiro."));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.System, "Você nunca cria respostas em inglês, mas sempre em português brasileiro."));
|
||||||
|
msg.Add(new ChatMessageContent(AuthorRole.User, "Responda sempre em portugues do Brasil as minhas perguntas."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
ChatHistoryStore.cs
Normal file
7
ChatHistoryStore.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace ChatApi
|
||||||
|
{
|
||||||
|
public class ChatHistoryStore
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
32
ChatRAG.csproj
Normal file
32
ChatRAG.csproj
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>10e5023f-8f45-46d6-8637-bc2127842068</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
<DockerfileContext>.</DockerfileContext>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Lost.SemanticKernel.Connectors.Anthropic" Version="1.25.0-alpha3" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.AI.Ollama" Version="9.0.0-preview.9.24525.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.VectorData.Abstractions" Version="9.0.0-preview.1.24523.1" />
|
||||||
|
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.2.1" />
|
||||||
|
<PackageReference Include="Microsoft.SemanticKernel" Version="1.26.0" />
|
||||||
|
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.26.0-alpha" />
|
||||||
|
<PackageReference Include="Microsoft.SemanticKernel.Connectors.InMemory" Version="1.26.0-preview" />
|
||||||
|
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.26.0-alpha" />
|
||||||
|
<PackageReference Include="Microsoft.SemanticKernel.Connectors.OpenAI" Version="1.26.0" />
|
||||||
|
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.26.0" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||||
|
<PackageReference Include="MongoDB.Driver" Version="3.0.0" />
|
||||||
|
<PackageReference Include="MongoDB.Driver.Core" Version="2.30.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
11
ChatRAG.csproj.user
Normal file
11
ChatRAG.csproj.user
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ActiveDebugProfile>http</ActiveDebugProfile>
|
||||||
|
<Controller_SelectedScaffolderID>ApiControllerEmptyScaffolder</Controller_SelectedScaffolderID>
|
||||||
|
<Controller_SelectedScaffolderCategoryPath>root/Common/Api</Controller_SelectedScaffolderCategoryPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
25
ChatRAG.sln
Normal file
25
ChatRAG.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.10.35122.118
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatRAG", "ChatRAG.csproj", "{B5287933-4BFA-4EC1-8522-393864C46B1F}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B5287933-4BFA-4EC1-8522-393864C46B1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B5287933-4BFA-4EC1-8522-393864C46B1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B5287933-4BFA-4EC1-8522-393864C46B1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B5287933-4BFA-4EC1-8522-393864C46B1F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {332EC001-9CEA-4261-9DE0-110EEB502728}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
108
Controllers/ChatController.cs
Normal file
108
Controllers/ChatController.cs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using ChatApi.Services;
|
||||||
|
using ChatApi.Services.ResponseService;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using ChatApi.Models;
|
||||||
|
using ChatApi.Data;
|
||||||
|
using ChatRAG.Services.ResponseService;
|
||||||
|
using ChatRAG.Services;
|
||||||
|
|
||||||
|
#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
|
|
||||||
|
namespace ChatApi.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("[controller]")]
|
||||||
|
//[Authorize]
|
||||||
|
public class ChatController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ILogger<ChatController> _logger;
|
||||||
|
private readonly IResponseService _responseService;
|
||||||
|
private readonly TextFilter _textFilter;
|
||||||
|
private readonly UserDataRepository _userDataRepository;
|
||||||
|
private readonly TextData _textData;
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
|
||||||
|
public ChatController(
|
||||||
|
ILogger<ChatController> logger,
|
||||||
|
IResponseService responseService,
|
||||||
|
UserDataRepository userDataRepository,
|
||||||
|
TextData textData,
|
||||||
|
IHttpClientFactory httpClientFactory)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_responseService = responseService;
|
||||||
|
_userDataRepository = userDataRepository;
|
||||||
|
_textData = textData;
|
||||||
|
this._httpClientFactory = httpClientFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("response")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<string?> GetResponse([FromForm] ChatRequest chatRequest)
|
||||||
|
{
|
||||||
|
var userData = await _userDataRepository.GeByTokentAsync(AppDomain.CurrentDomain.GetData("Token") as string);
|
||||||
|
var response = await _responseService.GetResponse(userData, chatRequest.SessionId, chatRequest.Message);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("savetext")]
|
||||||
|
public async Task SaveSingleText([FromBody] TextRequest request)
|
||||||
|
{
|
||||||
|
await _textData.SalvarNoMongoDB(request.Id, request.Title, request.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("texts")]
|
||||||
|
public async Task<IEnumerable<TextResponse>> GetTexts()
|
||||||
|
{
|
||||||
|
var texts = await _textData.GetAll();
|
||||||
|
return texts.Select(t => {
|
||||||
|
return new TextResponse
|
||||||
|
{
|
||||||
|
Id = t.Id,
|
||||||
|
Title = t.Titulo,
|
||||||
|
Content = t.Conteudo
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("texts/id/{id}")]
|
||||||
|
public async Task<TextResponse> GetText([FromRoute] string id)
|
||||||
|
{
|
||||||
|
var textItem = await _textData.GetById(id);
|
||||||
|
|
||||||
|
return new TextResponse {
|
||||||
|
Id = textItem.Id,
|
||||||
|
Title = textItem.Titulo,
|
||||||
|
Content = textItem.Conteudo
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("health")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult Health()
|
||||||
|
{
|
||||||
|
return Ok("It´s online.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TextRequest
|
||||||
|
{
|
||||||
|
public string? Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
}
|
||||||
|
public class TextResponse
|
||||||
|
{
|
||||||
|
public string? Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
123
Controllers/LoginController.cs
Normal file
123
Controllers/LoginController.cs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
using ChatApi.Models;
|
||||||
|
using ChatApi.Services.Crypt;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Cors;
|
||||||
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ChatApi.Controllers
|
||||||
|
{
|
||||||
|
[Route("[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
[EnableCors("AllowSpecificOrigin")]
|
||||||
|
public class LoginController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IConfigurationManager _configuration;
|
||||||
|
private readonly UserDataRepository _userDataRepository;
|
||||||
|
private readonly CryptUtil _cryptUtil;
|
||||||
|
|
||||||
|
public LoginController(IConfigurationManager configuration, UserDataRepository userDataRepository, CryptUtil cryptUtil)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_userDataRepository = userDataRepository;
|
||||||
|
_cryptUtil = cryptUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpPost]
|
||||||
|
[Route("token")]
|
||||||
|
public async Task<IActionResult> Post([FromBody] LoginRequest loginRequest)
|
||||||
|
{
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var userDataFrom = await _userDataRepository.GetAsync(loginRequest.ClientName, loginRequest.ClientId, loginRequest.ClientSecret);
|
||||||
|
if (userDataFrom==null)
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
var token = "";
|
||||||
|
if (userDataFrom.LastToken == null && (userDataFrom.DateTimeToken != null && userDataFrom.DateTimeToken.Value.AddHours(24) > DateTime.Now))
|
||||||
|
{
|
||||||
|
token = userDataFrom.LastToken;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var claims = new[]
|
||||||
|
{
|
||||||
|
new Claim("Sub", userDataFrom.CompanyTenant),
|
||||||
|
new Claim("NameId", userDataFrom.Name),
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, loginRequest.ClientId),
|
||||||
|
new Claim("DhCriado", DateTime.Now.ToString(new CultureInfo("pt-BR"))),
|
||||||
|
new Claim("TenantId", userDataFrom.CompanyTenant),
|
||||||
|
new Claim(ClaimTypes.Role, "TeamsUser")
|
||||||
|
};
|
||||||
|
|
||||||
|
var expires = DateTime.UtcNow.AddMinutes(30);
|
||||||
|
var tokenGen = new JwtSecurityToken
|
||||||
|
(
|
||||||
|
issuer: _configuration["Issuer"],
|
||||||
|
audience: _configuration["Audience"],
|
||||||
|
claims: claims,
|
||||||
|
expires: expires,
|
||||||
|
notBefore: DateTime.UtcNow,
|
||||||
|
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["SigningKey"])),
|
||||||
|
SecurityAlgorithms.HmacSha256)
|
||||||
|
);
|
||||||
|
|
||||||
|
token = new JwtSecurityTokenHandler().WriteToken(tokenGen);
|
||||||
|
}
|
||||||
|
userDataFrom.LastToken = token;
|
||||||
|
userDataFrom.DateTimeToken = DateTime.Now;
|
||||||
|
await _userDataRepository.UpdateAsync(userDataFrom.Id, userDataFrom);
|
||||||
|
|
||||||
|
return Ok(new { token = token });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return StatusCode(500, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpPost]
|
||||||
|
[Route("newclient")]
|
||||||
|
public async Task<IActionResult> NewClient([FromBody] UserRequest userDataFrom)
|
||||||
|
{
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var userData = await _userDataRepository.GetAsync(userDataFrom.Name, userDataFrom.LocalId);
|
||||||
|
if (userData == null)
|
||||||
|
{
|
||||||
|
var secret = _cryptUtil.Encrypt(JsonSerializer.Serialize(userDataFrom));
|
||||||
|
userData = UserData.Create(userDataFrom, secret);
|
||||||
|
await _userDataRepository.CreateAsync(userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Created("newclient", userData);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return StatusCode(500, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Controllers/LoginRequest.cs
Normal file
9
Controllers/LoginRequest.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace ChatApi.Controllers
|
||||||
|
{
|
||||||
|
public class LoginRequest
|
||||||
|
{
|
||||||
|
public string ClientId { get; set; }
|
||||||
|
public string ClientName { get; set; }
|
||||||
|
public string ClientSecret { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Controllers/UserRequest.cs
Normal file
16
Controllers/UserRequest.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
using MongoDB.Bson;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ChatApi.Controllers
|
||||||
|
{
|
||||||
|
public class UserRequest
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string CompanyTenant { get; set; }
|
||||||
|
public string LocalId { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string? Secret { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
7
DBLoadRequest.cs
Normal file
7
DBLoadRequest.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace ChatApi
|
||||||
|
{
|
||||||
|
public class DBLoadRequest
|
||||||
|
{
|
||||||
|
public string Content { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
106
Data/TextData.cs
Normal file
106
Data/TextData.cs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
using ChatRAG.Models;
|
||||||
|
using ChatRAG.Repositories;
|
||||||
|
using Microsoft.SemanticKernel;
|
||||||
|
using Microsoft.SemanticKernel.Embeddings;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
|
|
||||||
|
namespace ChatApi.Data
|
||||||
|
{
|
||||||
|
public class TextData
|
||||||
|
{
|
||||||
|
private readonly ITextEmbeddingGenerationService _textEmbeddingGenerationService;
|
||||||
|
private readonly TextDataService _textDataService;
|
||||||
|
|
||||||
|
public TextData(ITextEmbeddingGenerationService textEmbeddingGenerationService, TextDataService textDataService)
|
||||||
|
{
|
||||||
|
_textEmbeddingGenerationService = textEmbeddingGenerationService;
|
||||||
|
_textDataService = textDataService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SalvarTextoComEmbeddingNoMongoDB(string textoCompleto)
|
||||||
|
{
|
||||||
|
var textoArray = new List<string>();
|
||||||
|
string[] textolinhas = textoCompleto.Split(
|
||||||
|
new string[] { "\n" },
|
||||||
|
StringSplitOptions.None
|
||||||
|
);
|
||||||
|
|
||||||
|
var title = textolinhas[0];
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
foreach (string line in textolinhas)
|
||||||
|
{
|
||||||
|
if (line.StartsWith("**") || line.StartsWith("\r**"))
|
||||||
|
{
|
||||||
|
if (builder.Length > 0)
|
||||||
|
{
|
||||||
|
textoArray.Add(title.Replace("**", "").Replace("\r", "") + ": " + Environment.NewLine + builder.ToString());
|
||||||
|
builder = new StringBuilder();
|
||||||
|
title = line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.AppendLine(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var item in textoArray)
|
||||||
|
{
|
||||||
|
await SalvarNoMongoDB(title, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SalvarNoMongoDB(string titulo, string texto)
|
||||||
|
{
|
||||||
|
await SalvarNoMongoDB(null, titulo, texto);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SalvarNoMongoDB(string? id, string titulo, string texto)
|
||||||
|
{
|
||||||
|
// Gerar embedding para o texto
|
||||||
|
var embedding = await _textEmbeddingGenerationService.GenerateEmbeddingAsync(texto);
|
||||||
|
|
||||||
|
// Converter embedding para um formato serializável (como um array de floats)
|
||||||
|
var embeddingArray = embedding.ToArray().Select(e => (double)e).ToArray();
|
||||||
|
|
||||||
|
var exists = id!=null ? await this.GetById(id) : null;
|
||||||
|
|
||||||
|
if (exists == null)
|
||||||
|
{
|
||||||
|
var documento = new TextoComEmbedding
|
||||||
|
{
|
||||||
|
Titulo = titulo,
|
||||||
|
Conteudo = texto,
|
||||||
|
Embedding = embeddingArray
|
||||||
|
};
|
||||||
|
|
||||||
|
await _textDataService.CreateAsync(documento);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var documento = new TextoComEmbedding
|
||||||
|
{
|
||||||
|
Id = id,
|
||||||
|
Titulo = titulo,
|
||||||
|
Conteudo = texto,
|
||||||
|
Embedding = embeddingArray
|
||||||
|
};
|
||||||
|
|
||||||
|
await _textDataService.UpdateAsync(id, documento);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<TextoComEmbedding>> GetAll()
|
||||||
|
{
|
||||||
|
return await _textDataService.GetAsync();
|
||||||
|
}
|
||||||
|
public async Task<TextoComEmbedding> GetById(string id)
|
||||||
|
{
|
||||||
|
return await _textDataService.GetAsync(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
45
Data/UserDataRepository.cs
Normal file
45
Data/UserDataRepository.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using ChatApi.Models;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
|
||||||
|
namespace ChatApi
|
||||||
|
{
|
||||||
|
public class UserDataRepository
|
||||||
|
{
|
||||||
|
private readonly IMongoCollection<UserData> _userCollection;
|
||||||
|
|
||||||
|
public UserDataRepository(
|
||||||
|
IOptions<DomvsDatabaseSettings> bookStoreDatabaseSettings)
|
||||||
|
{
|
||||||
|
var mongoClient = new MongoClient(
|
||||||
|
bookStoreDatabaseSettings.Value.ConnectionString);
|
||||||
|
|
||||||
|
var mongoDatabase = mongoClient.GetDatabase(
|
||||||
|
bookStoreDatabaseSettings.Value.DatabaseName);
|
||||||
|
|
||||||
|
_userCollection = mongoDatabase.GetCollection<UserData>(
|
||||||
|
bookStoreDatabaseSettings.Value.UserDataName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<UserData>> GetAsync() =>
|
||||||
|
await _userCollection.Find(_ => true).ToListAsync();
|
||||||
|
|
||||||
|
public async Task<UserData?> GeByTokentAsync(string token) =>
|
||||||
|
await _userCollection.Find(x => x.LastToken == token).FirstOrDefaultAsync();
|
||||||
|
public async Task<UserData?> GetAsync(string id) =>
|
||||||
|
await _userCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
|
||||||
|
public async Task<UserData?> GetAsync(string name, string localId) =>
|
||||||
|
await _userCollection.Find(x => x.Name == name && x.LocalId == localId).FirstOrDefaultAsync();
|
||||||
|
public async Task<UserData?> GetAsync(string name, string localId, string secret) =>
|
||||||
|
await _userCollection.Find(x => x.Name == name && x.LocalId == localId && x.Secret == secret).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
public async Task CreateAsync(UserData newBook) =>
|
||||||
|
await _userCollection.InsertOneAsync(newBook);
|
||||||
|
|
||||||
|
public async Task UpdateAsync(string id, UserData updatedBook) =>
|
||||||
|
await _userCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
|
||||||
|
|
||||||
|
public async Task RemoveAsync(string id) =>
|
||||||
|
await _userCollection.DeleteOneAsync(x => x.Id == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Dockerfile
Normal file
25
Dockerfile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||||
|
USER app
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 8080
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["ChatApi.csproj", "."]
|
||||||
|
RUN dotnet restore "./ChatApi.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/."
|
||||||
|
RUN dotnet build "./ChatApi.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "./ChatApi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "ChatApi.dll"]
|
||||||
17
DomvsDatabaseSettings.cs
Normal file
17
DomvsDatabaseSettings.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace ChatApi
|
||||||
|
{
|
||||||
|
public class DomvsDatabaseSettings
|
||||||
|
{
|
||||||
|
public string ConnectionString { get; set; } = null!;
|
||||||
|
|
||||||
|
public string DatabaseName { get; set; } = null!;
|
||||||
|
|
||||||
|
public string SharepointCollectionName { get; set; } = null!;
|
||||||
|
|
||||||
|
public string UserDataName { get; set; } = null!;
|
||||||
|
|
||||||
|
public string ChatBotRHCollectionName { get; set; } = null!;
|
||||||
|
|
||||||
|
public string ClassifierCollectionName { get; set; } = null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
56
Infra/Result.cs
Normal file
56
Infra/Result.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
namespace ChatApi.Infra
|
||||||
|
{
|
||||||
|
public class Result
|
||||||
|
{
|
||||||
|
protected Result(bool success, string error)
|
||||||
|
{
|
||||||
|
if (success && error != string.Empty)
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
if (!success && error == string.Empty)
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
Success = success;
|
||||||
|
Error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Success { get; }
|
||||||
|
public string Error { get; }
|
||||||
|
public bool IsFailure => !Success;
|
||||||
|
|
||||||
|
public static Result Fail(string message)
|
||||||
|
{
|
||||||
|
return new Result(false, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<T> Fail<T>(string message)
|
||||||
|
{
|
||||||
|
return new Result<T>(default, false, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result Ok()
|
||||||
|
{
|
||||||
|
return new Result(true, string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<T> Ok<T>(T value)
|
||||||
|
{
|
||||||
|
return new Result<T>(value, true, string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Result<T> : Result
|
||||||
|
{
|
||||||
|
protected internal Result(T value, bool success, string error)
|
||||||
|
: base(success, error)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal Result(T value)
|
||||||
|
: base(true, "")
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
47
Middlewares/ErrorHandlingMiddleware.cs
Normal file
47
Middlewares/ErrorHandlingMiddleware.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ChatApi.Middlewares
|
||||||
|
{
|
||||||
|
public class ErrorHandlingMiddleware
|
||||||
|
{
|
||||||
|
readonly RequestDelegate _next;
|
||||||
|
static ILogger<ErrorHandlingMiddleware> _logger;
|
||||||
|
|
||||||
|
public ErrorHandlingMiddleware(RequestDelegate next,
|
||||||
|
ILogger<ErrorHandlingMiddleware> logger)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await HandleExceptionAsync(context, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task HandleExceptionAsync(
|
||||||
|
HttpContext context,
|
||||||
|
Exception exception)
|
||||||
|
{
|
||||||
|
string error = "An internal server error has occured.";
|
||||||
|
|
||||||
|
_logger.LogError($"{exception.Source} - {exception.Message} - {exception.StackTrace} - {exception.TargetSite.Name}");
|
||||||
|
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
|
||||||
|
await context.Response.WriteAsync(JsonSerializer.Serialize(new
|
||||||
|
{
|
||||||
|
error
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Models/TextoComEmbedding.cs
Normal file
25
Models/TextoComEmbedding.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
using MongoDB.Bson;
|
||||||
|
|
||||||
|
namespace ChatRAG.Models
|
||||||
|
{
|
||||||
|
public class TextoComEmbedding
|
||||||
|
{
|
||||||
|
[BsonId]
|
||||||
|
[BsonElement("_id")]
|
||||||
|
[BsonRepresentation(BsonType.ObjectId)]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public string Titulo { get; set; }
|
||||||
|
//public string Título { get; set; }
|
||||||
|
public string Conteudo { get; set; }
|
||||||
|
public double[] Embedding { get; set; }
|
||||||
|
|
||||||
|
public string ProjetoNome { get; set; }
|
||||||
|
public string ProjetoId { get; set; } // Para referência se tiver tabela de projetos
|
||||||
|
public string TipoDocumento { get; set; } // "requisitos", "arquitetura", "casos_teste", etc.
|
||||||
|
public string Categoria { get; set; } // "login", "relatorios", "api", etc.
|
||||||
|
public DateTime DataCriacao { get; set; }
|
||||||
|
public string[] Tags { get; set; } // Para busca adicional
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Models/UserData.cs
Normal file
38
Models/UserData.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
using MongoDB.Bson;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using ChatApi.Controllers;
|
||||||
|
|
||||||
|
namespace ChatApi.Models
|
||||||
|
{
|
||||||
|
public class UserData
|
||||||
|
{
|
||||||
|
public UserData()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BsonId]
|
||||||
|
[BsonElement("_id")]
|
||||||
|
[BsonRepresentation(BsonType.String)]
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string LocalId { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Secret { get; set; }
|
||||||
|
public string CompanyTenant { get; set; }
|
||||||
|
public string? Email { get; set; }
|
||||||
|
public string? LastToken { get; set; }
|
||||||
|
public DateTime? DateTimeToken { get; set; }
|
||||||
|
|
||||||
|
public static UserData Create(UserRequest userRequest, string secret)
|
||||||
|
{
|
||||||
|
return new UserData
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid().ToString("N"),
|
||||||
|
Name = userRequest.Name,
|
||||||
|
CompanyTenant = userRequest.CompanyTenant,
|
||||||
|
LocalId = userRequest.LocalId,
|
||||||
|
Secret = secret
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
244
Program.cs
Normal file
244
Program.cs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
using ChatApi;
|
||||||
|
using ChatApi.Data;
|
||||||
|
using ChatApi.Middlewares;
|
||||||
|
using ChatApi.Services.Crypt;
|
||||||
|
using ChatApi.Settings;
|
||||||
|
using ChatRAG.Repositories;
|
||||||
|
using ChatRAG.Services;
|
||||||
|
using ChatRAG.Services.ResponseService;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
using Microsoft.IdentityModel.JsonWebTokens;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Microsoft.SemanticKernel;
|
||||||
|
using System.Text;
|
||||||
|
using static OllamaSharp.OllamaApiClient;
|
||||||
|
using static System.Net.Mime.MediaTypeNames;
|
||||||
|
|
||||||
|
#pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
|
||||||
|
// Adicionar serviço CORS
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("AllowSpecificOrigin",
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.WithOrigins("http://localhost:5094") // Sua origem específica
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowCredentials();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "apichat", Version = "v1" });
|
||||||
|
|
||||||
|
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
|
||||||
|
{
|
||||||
|
Name = "Authorization",
|
||||||
|
Type = SecuritySchemeType.ApiKey,
|
||||||
|
Scheme = "Bearer",
|
||||||
|
BearerFormat = "JWT",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer'[space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\"",
|
||||||
|
});
|
||||||
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Type = ReferenceType.SecurityScheme,
|
||||||
|
Id = "Bearer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new string[] {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.Configure<DomvsDatabaseSettings>(
|
||||||
|
builder.Configuration.GetSection("DomvsDatabase"));
|
||||||
|
|
||||||
|
builder.Services.Configure<ChatRHSettings>(
|
||||||
|
builder.Configuration.GetSection("ChatRHSettings"));
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<ChatHistoryService>();
|
||||||
|
builder.Services.AddScoped<TextDataService>();
|
||||||
|
builder.Services.AddSingleton<TextFilter>();
|
||||||
|
|
||||||
|
builder.Services.AddScoped<IResponseService, ResponseRAGService>();
|
||||||
|
builder.Services.AddTransient<UserDataRepository>();
|
||||||
|
builder.Services.AddTransient<TextData>();
|
||||||
|
builder.Services.AddSingleton<CryptUtil>();
|
||||||
|
|
||||||
|
|
||||||
|
//builder.Services.AddOllamaChatCompletion("phi3.5", new Uri("http://localhost:11435"));
|
||||||
|
//builder.Services.AddOllamaChatCompletion("tinydolphin", new Uri("http://localhost:11435"));
|
||||||
|
//var apiClient = new OllamaApiClient(new Uri("http://localhost:11435"), "tinydolphin");
|
||||||
|
|
||||||
|
//Olllama notebook
|
||||||
|
//builder.Services.AddOllamaChatCompletion("phi3.5", new Uri("http://localhost:11435"));
|
||||||
|
|
||||||
|
//builder.Services.AddOllamaChatCompletion("tinydolphin", new Uri("http://localhost:11435"));
|
||||||
|
//builder.Services.AddOllamaChatCompletion("tinyllama", new Uri("http://localhost:11435"));
|
||||||
|
//builder.Services.AddOllamaChatCompletion("starling-lm", new Uri("http://localhost:11435"));
|
||||||
|
|
||||||
|
//ServerSpace - GPT Service
|
||||||
|
builder.Services.AddOpenAIChatCompletion("openchat-3.5-0106", new Uri("https://gpt.serverspace.com.br/v1/chat/completions"), "tIAXVf3AkCkkpSX+PjFvktfEeSPyA1ZYam50UO3ye/qmxVZX6PIXstmJsLZXkQ39C33onFD/81mdxvhbGHm7tQ==");
|
||||||
|
|
||||||
|
//Ollama local server (scorpion)
|
||||||
|
//builder.Services.AddOllamaChatCompletion("llama3.1:latest", new Uri("http://192.168.0.150:11434"));
|
||||||
|
|
||||||
|
builder.Services.AddOllamaTextEmbeddingGeneration("all-minilm", new Uri("http://192.168.0.150:11434"));
|
||||||
|
//builder.Services.AddOllamaChatCompletion("phi3.5", new Uri("http://localhost:11435"));
|
||||||
|
//builder.Services.AddOpenAIChatCompletion("gpt-4o-mini", "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");
|
||||||
|
//builder.Services.AddGoogleAIGeminiChatCompletion("gemini-1.5-flash-latest", "AIzaSyDKBMX5yW77vxJFVJVE-5VLxlQRxCepck8");
|
||||||
|
|
||||||
|
//Anthropic / Claude
|
||||||
|
//builder.Services.AddAnthropicChatCompletion(
|
||||||
|
// modelId: "claude-3-5-sonnet-latest", // ou outro modelo Claude desejado
|
||||||
|
// apiKey: "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"
|
||||||
|
//);
|
||||||
|
|
||||||
|
|
||||||
|
builder.Services.AddKernel();
|
||||||
|
|
||||||
|
//builder.Services.AddKernel()
|
||||||
|
// .AddOllamaChatCompletion("phi3", new Uri("http://localhost:11435"))
|
||||||
|
// .AddOllamaTextEmbeddingGeneration()
|
||||||
|
// .Build();
|
||||||
|
|
||||||
|
//builder.Services.AddOllamaChatCompletion("phi3.5", new Uri("http://192.168.0.150:11436"));
|
||||||
|
|
||||||
|
builder.Services.AddHttpClient();
|
||||||
|
|
||||||
|
var tenantId = builder.Configuration.GetSection("AppTenantId");
|
||||||
|
var clientId = builder.Configuration.GetSection("AppClientID");
|
||||||
|
|
||||||
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer(jwtBearerOptions =>
|
||||||
|
{
|
||||||
|
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters()
|
||||||
|
{
|
||||||
|
ValidateActor = true,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
ValidIssuer = builder.Configuration["Issuer"],
|
||||||
|
ValidAudience = builder.Configuration["Audience"],
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["SigningKey"]))
|
||||||
|
};
|
||||||
|
jwtBearerOptions.Events = new JwtBearerEvents()
|
||||||
|
{
|
||||||
|
OnTokenValidated = async context =>
|
||||||
|
{
|
||||||
|
var token = context.SecurityToken as JsonWebToken;
|
||||||
|
context.HttpContext.Items["Token"] = token.EncodedToken;
|
||||||
|
AppDomain.CurrentDomain.SetData("Token", token.EncodedToken);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
//builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
// .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
|
||||||
|
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
|
//builder.Services.AddAuthentication(options =>
|
||||||
|
// {
|
||||||
|
// options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
// options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
// })
|
||||||
|
// .AddJwtBearer(options =>
|
||||||
|
// {
|
||||||
|
// // Configurações anteriores...
|
||||||
|
|
||||||
|
// // Eventos para log e tratamento de erros
|
||||||
|
// options.Events = new JwtBearerEvents
|
||||||
|
// {
|
||||||
|
// OnAuthenticationFailed = context =>
|
||||||
|
// {
|
||||||
|
// // Log de erros de autenticação
|
||||||
|
// Console.WriteLine($"Erro de autenticação: {context.Exception.Message}");
|
||||||
|
// return Task.CompletedTask;
|
||||||
|
// },
|
||||||
|
// OnTokenValidated = context =>
|
||||||
|
// {
|
||||||
|
// // Validações adicionais se necessário
|
||||||
|
// return Task.CompletedTask;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<IConfigurationManager>(builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.Configure<IISServerOptions>(options =>
|
||||||
|
{
|
||||||
|
options.MaxRequestBodySize = int.MaxValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.Configure<KestrelServerOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Limits.MaxRequestBodySize = int.MaxValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.Configure<FormOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ValueLengthLimit = int.MaxValue;
|
||||||
|
options.MultipartBodyLengthLimit = int.MaxValue;
|
||||||
|
options.MultipartHeadersLengthLimit = int.MaxValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
//app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
app.Use(async (context, next) =>
|
||||||
|
{
|
||||||
|
var cookieOpt = new CookieOptions()
|
||||||
|
{
|
||||||
|
Path = "/",
|
||||||
|
Expires = DateTimeOffset.UtcNow.AddDays(1),
|
||||||
|
IsEssential = true,
|
||||||
|
HttpOnly = false,
|
||||||
|
Secure = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseMiddleware<ErrorHandlingMiddleware>();
|
||||||
|
|
||||||
|
app.UseCors("AllowSpecificOrigin");
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
||||||
|
#pragma warning restore SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
52
Properties/launchSettings.json
Normal file
52
Properties/launchSettings.json
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "http://localhost:5020"
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "https://localhost:7163;http://localhost:5020"
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Container (Dockerfile)": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_HTTPS_PORTS": "8081",
|
||||||
|
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||||
|
},
|
||||||
|
"publishAllPorts": true,
|
||||||
|
"useSSL": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:61198",
|
||||||
|
"sslPort": 44305
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
PushContainer.bat
Normal file
4
PushContainer.bat
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
cd C:\vscode\ChatApi
|
||||||
|
docker build -t chat-api .
|
||||||
|
docker tag chat-api:latest registry.redecarneir.us/chat-api:latest
|
||||||
|
docker push registry.redecarneir.us/chat-api:latest
|
||||||
40
Repositories/TextDataService.cs
Normal file
40
Repositories/TextDataService.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using ChatApi;
|
||||||
|
using ChatRAG.Models;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
|
||||||
|
namespace ChatRAG.Repositories
|
||||||
|
{
|
||||||
|
public class TextDataService
|
||||||
|
{
|
||||||
|
private readonly IMongoCollection<TextoComEmbedding> _textsCollection;
|
||||||
|
|
||||||
|
public TextDataService(
|
||||||
|
IOptions<DomvsDatabaseSettings> bookStoreDatabaseSettings)
|
||||||
|
{
|
||||||
|
var mongoClient = new MongoClient(
|
||||||
|
bookStoreDatabaseSettings.Value.ConnectionString);
|
||||||
|
|
||||||
|
var mongoDatabase = mongoClient.GetDatabase(
|
||||||
|
bookStoreDatabaseSettings.Value.DatabaseName);
|
||||||
|
|
||||||
|
_textsCollection = mongoDatabase.GetCollection<TextoComEmbedding>(
|
||||||
|
bookStoreDatabaseSettings.Value.SharepointCollectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<TextoComEmbedding>> GetAsync() =>
|
||||||
|
await _textsCollection.Find(_ => true).ToListAsync();
|
||||||
|
|
||||||
|
public async Task<TextoComEmbedding?> GetAsync(string id) =>
|
||||||
|
await _textsCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
public async Task CreateAsync(TextoComEmbedding newBook) =>
|
||||||
|
await _textsCollection.InsertOneAsync(newBook);
|
||||||
|
|
||||||
|
public async Task UpdateAsync(string id, TextoComEmbedding updatedBook) =>
|
||||||
|
await _textsCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
|
||||||
|
|
||||||
|
public async Task RemoveAsync(string id) =>
|
||||||
|
await _textsCollection.DeleteOneAsync(x => x.Id == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Requests/ChatRequest.cs
Normal file
15
Requests/ChatRequest.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
public class ChatRequest
|
||||||
|
{
|
||||||
|
public string SessionId { get; set; } = string.Empty;
|
||||||
|
public string Message { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class VideoSummaryRequest
|
||||||
|
{
|
||||||
|
public string SessionId { get; set; } = string.Empty;
|
||||||
|
public string Url { get; set; } = string.Empty;
|
||||||
|
public string Language { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
9
Requests/FileData.cs
Normal file
9
Requests/FileData.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace ChatApi.Requests
|
||||||
|
{
|
||||||
|
public class FileData
|
||||||
|
{
|
||||||
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
public string ContentType { get; set; } = string.Empty;
|
||||||
|
public byte[] Content { get; set; } = Array.Empty<byte>();
|
||||||
|
}
|
||||||
|
}
|
||||||
66
Services/Crypt/CryptUtil.cs
Normal file
66
Services/Crypt/CryptUtil.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ChatApi.Services.Crypt
|
||||||
|
{
|
||||||
|
public class CryptUtil
|
||||||
|
{
|
||||||
|
private readonly IConfigurationManager _configuration;
|
||||||
|
|
||||||
|
public CryptUtil(IConfigurationManager configuration)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
}
|
||||||
|
public string Encrypt(string text)
|
||||||
|
{
|
||||||
|
string key = _configuration.GetSection("DataKey").Value;
|
||||||
|
using (Aes aes = Aes.Create())
|
||||||
|
{
|
||||||
|
aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32));
|
||||||
|
aes.GenerateIV(); // Cria um vetor de inicialização aleatório
|
||||||
|
byte[] iv = aes.IV;
|
||||||
|
|
||||||
|
using (var encryptor = aes.CreateEncryptor(aes.Key, iv))
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
// Primeiro, escreva o IV para o fluxo (será necessário para descriptografia)
|
||||||
|
ms.Write(iv, 0, iv.Length);
|
||||||
|
|
||||||
|
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
|
||||||
|
using (var writer = new StreamWriter(cs))
|
||||||
|
{
|
||||||
|
writer.Write(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Convert.ToBase64String(ms.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Descrypt(string text)
|
||||||
|
{
|
||||||
|
string key = _configuration.GetSection("DataKey").Value;
|
||||||
|
byte[] bytes = Convert.FromBase64String(text);
|
||||||
|
|
||||||
|
using (Aes aes = Aes.Create())
|
||||||
|
{
|
||||||
|
aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32));
|
||||||
|
|
||||||
|
// Extrair o IV do início dos dados criptografados
|
||||||
|
byte[] iv = new byte[aes.BlockSize / 8];
|
||||||
|
Array.Copy(bytes, 0, iv, 0, iv.Length);
|
||||||
|
aes.IV = iv;
|
||||||
|
|
||||||
|
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
|
||||||
|
using (var ms = new MemoryStream(bytes, iv.Length, bytes.Length - iv.Length))
|
||||||
|
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
|
||||||
|
using (var reader = new StreamReader(cs))
|
||||||
|
{
|
||||||
|
return reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Services/Emails/EmailValidate.cs
Normal file
24
Services/Emails/EmailValidate.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
namespace ChatApi.Services.Emails
|
||||||
|
{
|
||||||
|
public class EmailValidate
|
||||||
|
{
|
||||||
|
public static bool IsValidEmail(string email)
|
||||||
|
{
|
||||||
|
var trimmedEmail = email.Trim();
|
||||||
|
|
||||||
|
if (trimmedEmail.EndsWith("."))
|
||||||
|
{
|
||||||
|
return false; // suggested by @TK-421
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var addr = new System.Net.Mail.MailAddress(email);
|
||||||
|
return addr.Address == trimmedEmail;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Services/ResponseService/IResponseService.cs
Normal file
9
Services/ResponseService/IResponseService.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using ChatApi.Models;
|
||||||
|
|
||||||
|
namespace ChatRAG.Services.ResponseService
|
||||||
|
{
|
||||||
|
public interface IResponseService
|
||||||
|
{
|
||||||
|
Task<string> GetResponse(UserData userData, string sessionId, string question);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Services/ResponseService/IResponseWithFiles.cs
Normal file
9
Services/ResponseService/IResponseWithFiles.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using ChatApi.Models;
|
||||||
|
|
||||||
|
namespace ChatApi.Services.ResponseService
|
||||||
|
{
|
||||||
|
public interface IResponseWithFiles
|
||||||
|
{
|
||||||
|
Task<string> GetResponse(HttpContext context, UserData userData, string sessionId, string question, List<IFormFile>? files = null, bool needsRestart = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
132
Services/ResponseService/ResponseCompanyService.cs
Normal file
132
Services/ResponseService/ResponseCompanyService.cs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
|
||||||
|
using ChatApi;
|
||||||
|
using ChatApi.Models;
|
||||||
|
using ChatRAG.Models;
|
||||||
|
using ChatRAG.Repositories;
|
||||||
|
using Microsoft.SemanticKernel;
|
||||||
|
using Microsoft.SemanticKernel.ChatCompletion;
|
||||||
|
using Microsoft.SemanticKernel.Embeddings;
|
||||||
|
|
||||||
|
#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
|
|
||||||
|
namespace ChatRAG.Services.ResponseService
|
||||||
|
{
|
||||||
|
public class ResponseRAGService : IResponseService
|
||||||
|
{
|
||||||
|
private readonly ChatHistoryService _chatHistoryService;
|
||||||
|
private readonly Kernel _kernel;
|
||||||
|
private readonly TextFilter _textFilter;
|
||||||
|
private readonly TextDataService _textDataService;
|
||||||
|
private readonly IChatCompletionService _chatCompletionService;
|
||||||
|
|
||||||
|
public ResponseRAGService(
|
||||||
|
ChatHistoryService chatHistoryService,
|
||||||
|
Kernel kernel,
|
||||||
|
TextFilter textFilter,
|
||||||
|
TextDataService textDataService,
|
||||||
|
IChatCompletionService chatCompletionService)
|
||||||
|
{
|
||||||
|
this._chatHistoryService = chatHistoryService;
|
||||||
|
this._kernel = kernel;
|
||||||
|
this._textFilter = textFilter;
|
||||||
|
this._textDataService = textDataService;
|
||||||
|
this._chatCompletionService = chatCompletionService;
|
||||||
|
}
|
||||||
|
public async Task<string> GetResponse(UserData userData, string sessionId, string question)
|
||||||
|
{
|
||||||
|
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||||
|
stopWatch.Start();
|
||||||
|
|
||||||
|
//var resposta = await BuscarTextoRelacionado(question);
|
||||||
|
var resposta = await BuscarTopTextosRelacionados(question);
|
||||||
|
|
||||||
|
question = "Para responder à solicitação/pergunta: \"" + question + "\" por favor, considere um resumo com 3 linhas baseado exclusivamente no texto: \"" + resposta + "\"";
|
||||||
|
ChatHistory history = _chatHistoryService.GetSumarizer(sessionId);
|
||||||
|
|
||||||
|
history.AddUserMessage(question);
|
||||||
|
|
||||||
|
var response = await _chatCompletionService.GetChatMessageContentAsync(history);
|
||||||
|
history.AddMessage(response.Role, response.Content ?? "");
|
||||||
|
|
||||||
|
_chatHistoryService.UpdateHistory(sessionId, history);
|
||||||
|
|
||||||
|
stopWatch.Stop();
|
||||||
|
return $"{response.Content ?? ""}\n\nTempo: {stopWatch.ElapsedMilliseconds / 1000}s";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<string> BuscarTextoRelacionado(string pergunta)
|
||||||
|
{
|
||||||
|
var embeddingService = _kernel.GetRequiredService<ITextEmbeddingGenerationService>();
|
||||||
|
var embeddingPergunta = await embeddingService.GenerateEmbeddingAsync(_textFilter.ToLowerAndWithoutAccents(pergunta));
|
||||||
|
var embeddingArrayPergunta = embeddingPergunta.ToArray().Select(e => (double)e).ToArray();
|
||||||
|
|
||||||
|
var textos = await _textDataService.GetAsync();
|
||||||
|
|
||||||
|
TextoComEmbedding melhorTexto = null;
|
||||||
|
double melhorSimilaridade = -1.0;
|
||||||
|
|
||||||
|
foreach (var texto in textos)
|
||||||
|
{
|
||||||
|
double similaridade = CalcularSimilaridadeCoseno(embeddingArrayPergunta, texto.Embedding);
|
||||||
|
if (similaridade > melhorSimilaridade)
|
||||||
|
{
|
||||||
|
melhorSimilaridade = similaridade;
|
||||||
|
melhorTexto = texto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return melhorTexto != null ? melhorTexto.Conteudo : "Não encontrei uma resposta adequada.";
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<string> BuscarTopTextosRelacionados(string pergunta, int size = 3)
|
||||||
|
{
|
||||||
|
var embeddingService = _kernel.GetRequiredService<ITextEmbeddingGenerationService>();
|
||||||
|
var embeddingPergunta = await embeddingService.GenerateEmbeddingAsync(_textFilter.ToLowerAndWithoutAccents(pergunta));
|
||||||
|
var embeddingArrayPergunta = embeddingPergunta.ToArray().Select(e => (double)e).ToArray();
|
||||||
|
|
||||||
|
var textos = await _textDataService.GetAsync();
|
||||||
|
|
||||||
|
var melhoresTextos = textos
|
||||||
|
.Select(texto => new
|
||||||
|
{
|
||||||
|
Conteudo = texto.Conteudo,
|
||||||
|
Similaridade = CalcularSimilaridadeCoseno(embeddingArrayPergunta, texto.Embedding)
|
||||||
|
})
|
||||||
|
.Where(x => x.Similaridade > 0.3)
|
||||||
|
.OrderByDescending(x => x.Similaridade)
|
||||||
|
.Take(3)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (!melhoresTextos.Any())
|
||||||
|
return "Não encontrei respostas adequadas para a pergunta fornecida.";
|
||||||
|
|
||||||
|
var cabecalho = $"Contexto encontrado para: '{pergunta}' ({melhoresTextos.Count} resultado(s)):\n\n";
|
||||||
|
|
||||||
|
var resultadosFormatados = melhoresTextos
|
||||||
|
.Select((item, index) =>
|
||||||
|
$"=== CONTEXTO {index + 1} ===\n" +
|
||||||
|
$"Relevância: {item.Similaridade:P1}\n" +
|
||||||
|
$"Conteúdo:\n{item.Conteudo}")
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return cabecalho + string.Join("\n\n", resultadosFormatados);
|
||||||
|
}
|
||||||
|
|
||||||
|
double CalcularSimilaridadeCoseno(double[] embedding1, double[] embedding2)
|
||||||
|
{
|
||||||
|
double dotProduct = 0.0;
|
||||||
|
double normA = 0.0;
|
||||||
|
double normB = 0.0;
|
||||||
|
for (int i = 0; i < embedding1.Length; i++)
|
||||||
|
{
|
||||||
|
dotProduct += embedding1[i] * embedding2[i];
|
||||||
|
normA += Math.Pow(embedding1[i], 2);
|
||||||
|
normB += Math.Pow(embedding2[i], 2);
|
||||||
|
}
|
||||||
|
return dotProduct / (Math.Sqrt(normA) * Math.Sqrt(normB));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
33
Services/TextFilter.cs
Normal file
33
Services/TextFilter.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ChatRAG.Services
|
||||||
|
{
|
||||||
|
public class TextFilter
|
||||||
|
{
|
||||||
|
public string ToLowerAndWithoutAccents(string text)
|
||||||
|
{
|
||||||
|
return RemoveDiacritics(text.ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RemoveDiacritics(string text)
|
||||||
|
{
|
||||||
|
var normalizedString = text.Normalize(NormalizationForm.FormD);
|
||||||
|
var stringBuilder = new StringBuilder(capacity: normalizedString.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < normalizedString.Length; i++)
|
||||||
|
{
|
||||||
|
char c = normalizedString[i];
|
||||||
|
var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
|
||||||
|
if (unicodeCategory != UnicodeCategory.NonSpacingMark)
|
||||||
|
{
|
||||||
|
stringBuilder.Append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringBuilder
|
||||||
|
.ToString()
|
||||||
|
.Normalize(NormalizationForm.FormC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Settings/ChatRHSettings.cs
Normal file
8
Settings/ChatRHSettings.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace ChatApi.Settings
|
||||||
|
{
|
||||||
|
public class ChatRHSettings
|
||||||
|
{
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string Create { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
20
appsettings.Development.json
Normal file
20
appsettings.Development.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"DomvsDatabase": {
|
||||||
|
//"ConnectionString": "mongodb://192.168.0.82:30017/?directConnection=true",
|
||||||
|
"ConnectionString": "mongodb://localhost:27017/?directConnection=true",
|
||||||
|
"DatabaseName": "DomvsSites",
|
||||||
|
"SharepointCollectionName": "SharepointSite",
|
||||||
|
"ChatBotRHCollectionName": "ChatBotRHData",
|
||||||
|
"ClassifierCollectionName": "ClassifierData"
|
||||||
|
},
|
||||||
|
"ChatRHSettings": {
|
||||||
|
"Url": "http://localhost:8070/",
|
||||||
|
"Create": "/CallRH"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
appsettings.json
Normal file
35
appsettings.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"DomvsDatabase": {
|
||||||
|
"ConnectionString": "mongodb://192.168.0.82:30017/?directConnection=true",
|
||||||
|
"DatabaseName": "DomvsSites",
|
||||||
|
"SharepointCollectionName": "SharepointSite",
|
||||||
|
"ChatBotRHCollectionName": "ChatBotRHData",
|
||||||
|
"ClassifierCollectionName": "ClassifierData",
|
||||||
|
"UserDataName": "UserData"
|
||||||
|
},
|
||||||
|
"ChatRHSettings": {
|
||||||
|
"Url": "http://apirhcall.lhost.dynu.net",
|
||||||
|
"Create": "/CallRH"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning",
|
||||||
|
"Microsoft.AspNetCore.DataProtection": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"AppTenantId": "20190830-5fd4-4a72-b8fd-1c1cb35b25bc",
|
||||||
|
"AppClientID": "8f4248fc-ee30-4f54-8793-66edcca3fd20",
|
||||||
|
|
||||||
|
"AzureAd": {
|
||||||
|
"Instance": "https://login.microsoftonline.com/",
|
||||||
|
"Domain": "domvsitbr.onmicrosoft.com",
|
||||||
|
"TenantId": "20190830-5fd4-4a72-b8fd-1c1cb35b25bc",
|
||||||
|
"ClientId": "8f4248fc-ee30-4f54-8793-66edcca3fd20"
|
||||||
|
},
|
||||||
|
"Issuer": "domvsit.com.br",
|
||||||
|
"Audience": "domvsit.com.br",
|
||||||
|
"SigningKey": "D57Ls16KxMPdF4P7qTQtV29slWjJqIJZ",
|
||||||
|
"DataKey": "NOxGacYtZRJTYCPdSQM75HVSNp3qfH05mPalaE/pL4A6FwxWKQiBhLxhu++LrKsI"
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user