UPDATE: Chatbot module
All checks were successful
Build and Release / release (push) Successful in 2m13s
All checks were successful
Build and Release / release (push) Successful in 2m13s
This commit is contained in:
51
internal/services/chatbotService.go
Normal file
51
internal/services/chatbotService.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"history-api/internal/repositories"
|
||||
"history-api/pkg/ai"
|
||||
)
|
||||
|
||||
type ChatbotService interface {
|
||||
Chat(ctx context.Context, projectID *string, question string) (string, error)
|
||||
}
|
||||
|
||||
type chatbotService struct {
|
||||
repo repositories.RagRepository
|
||||
ragUtils *ai.RagUtils
|
||||
}
|
||||
|
||||
func NewChatbotService(repo repositories.RagRepository, ragUtils *ai.RagUtils) ChatbotService {
|
||||
return &chatbotService{
|
||||
repo: repo,
|
||||
ragUtils: ragUtils,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *chatbotService) Chat(ctx context.Context, projectID *string, question string) (string, error) {
|
||||
qVector, err := s.ragUtils.EmbedQuery(ctx, question)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to embed question: %w", err)
|
||||
}
|
||||
results, err := s.repo.SearchSimilar(ctx, projectID, qVector, 5, 0.65)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to search similar content: %w", err)
|
||||
}
|
||||
|
||||
contextStr := ""
|
||||
for i, res := range results {
|
||||
contextStr += fmt.Sprintf("[Document %d]: %s\n", i+1, res.Content)
|
||||
}
|
||||
|
||||
prompt := fmt.Sprintf(`You are a helpful history assistant. Answer the question based ONLY on the provided context.
|
||||
If the answer is not in the context, say "I don't have enough historical context to answer that."
|
||||
|
||||
Context:
|
||||
%s
|
||||
|
||||
Question: %s
|
||||
Answer:`, contextStr, question)
|
||||
|
||||
return s.ragUtils.GenerateResponse(ctx, prompt)
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/internal/repositories"
|
||||
"history-api/pkg/ai"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
@@ -36,6 +37,8 @@ type submissionService struct {
|
||||
wikiRepo repositories.WikiRepository
|
||||
geometryRepo repositories.GeometryRepository
|
||||
entityRepo repositories.EntityRepository
|
||||
ragRepo repositories.RagRepository
|
||||
ragUtils *ai.RagUtils
|
||||
db *pgxpool.Pool
|
||||
c cache.Cache
|
||||
}
|
||||
@@ -48,6 +51,8 @@ func NewSubmissionService(
|
||||
wikiRepo repositories.WikiRepository,
|
||||
geometryRepo repositories.GeometryRepository,
|
||||
entityRepo repositories.EntityRepository,
|
||||
ragRepo repositories.RagRepository,
|
||||
ragUtils *ai.RagUtils,
|
||||
db *pgxpool.Pool,
|
||||
c cache.Cache,
|
||||
) SubmissionService {
|
||||
@@ -59,6 +64,8 @@ func NewSubmissionService(
|
||||
wikiRepo: wikiRepo,
|
||||
geometryRepo: geometryRepo,
|
||||
entityRepo: entityRepo,
|
||||
ragRepo: ragRepo,
|
||||
ragUtils: ragUtils,
|
||||
db: db,
|
||||
c: c,
|
||||
}
|
||||
@@ -127,6 +134,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
entityRepo := s.entityRepo.WithTx(tx)
|
||||
geometryRepo := s.geometryRepo.WithTx(tx)
|
||||
wikiRepo := s.wikiRepo.WithTx(tx)
|
||||
ragRepo := s.ragRepo.WithTx(tx)
|
||||
|
||||
submissionUUID, err := convert.StringToUUID(submissionID)
|
||||
if err != nil {
|
||||
@@ -166,8 +174,11 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Commit does not belong to project")
|
||||
}
|
||||
|
||||
listDeleteEntities := make([]pgtype.UUID, 0)
|
||||
listDeleteWikis := make([]pgtype.UUID, 0)
|
||||
listDeleteGeometries := make([]pgtype.UUID, 0)
|
||||
var snapshotData request.CommitSnapshot
|
||||
if status == constants.StatusTypeApproved {
|
||||
var snapshotData request.CommitSnapshot
|
||||
err = json.Unmarshal(commit.SnapshotJson, &snapshotData)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to parse commit snapshot")
|
||||
@@ -214,7 +225,6 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
persistCurrentItemIDs[item.ID] = struct{}{}
|
||||
}
|
||||
|
||||
listDeleteEntities := make([]pgtype.UUID, 0)
|
||||
for _, e := range currentEntity {
|
||||
if _, ok := persistItemIDs[e.ID]; !ok {
|
||||
itemUUID, err := convert.StringToUUID(e.ID)
|
||||
@@ -226,7 +236,6 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
}
|
||||
}
|
||||
|
||||
listDeleteGeometries := make([]pgtype.UUID, 0)
|
||||
for _, g := range currentGeometry {
|
||||
if _, ok := persistItemIDs[g.ID]; !ok {
|
||||
itemUUID, err := convert.StringToUUID(g.ID)
|
||||
@@ -238,7 +247,6 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
}
|
||||
}
|
||||
|
||||
listDeleteWikis := make([]pgtype.UUID, 0)
|
||||
for _, w := range currentWiki {
|
||||
if _, ok := persistItemIDs[w.ID]; !ok {
|
||||
itemUUID, err := convert.StringToUUID(w.ID)
|
||||
@@ -274,6 +282,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
refEntityIDs = append(refEntityIDs, e.ID)
|
||||
}
|
||||
}
|
||||
|
||||
refEntities, _ := s.entityRepo.GetByIDs(ctx, refEntityIDs)
|
||||
refEntityMap := make(map[string]bool)
|
||||
for _, e := range refEntities {
|
||||
@@ -444,7 +453,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
_, err := wikiRepo.Update(ctx, sqlc.UpdateWikiParams{
|
||||
ID: wikiUUID,
|
||||
Title: convert.StringToText(wiki.Title),
|
||||
Content: wiki.Doc,
|
||||
Content: convert.StringToText(wiki.Doc),
|
||||
ProjectID: projectUUID,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -456,7 +465,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
_, err := wikiRepo.Create(ctx, sqlc.CreateWikiParams{
|
||||
ID: wikiUUID,
|
||||
Title: convert.StringToText(wiki.Title),
|
||||
Content: wiki.Doc,
|
||||
Content: convert.StringToText(wiki.Doc),
|
||||
ProjectID: projectUUID,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -554,6 +563,58 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
}
|
||||
}
|
||||
|
||||
if status == constants.StatusTypeApproved {
|
||||
wikiDeleteIDs := make([]string, 0)
|
||||
entityDeleteIDs := make([]string, 0)
|
||||
|
||||
for _, id := range listDeleteWikis {
|
||||
wikiDeleteIDs = append(wikiDeleteIDs, convert.UUIDToString(id))
|
||||
}
|
||||
for _, id := range listDeleteEntities {
|
||||
entityDeleteIDs = append(entityDeleteIDs, convert.UUIDToString(id))
|
||||
}
|
||||
|
||||
for _, wiki := range snapshotData.Wikis {
|
||||
if wiki.Operation == "delete" {
|
||||
wikiDeleteIDs = append(wikiDeleteIDs, wiki.ID)
|
||||
}
|
||||
}
|
||||
for _, entity := range snapshotData.Entities {
|
||||
if entity.Operation == "delete" {
|
||||
entityDeleteIDs = append(entityDeleteIDs, entity.ID)
|
||||
}
|
||||
}
|
||||
|
||||
_ = ragRepo.DeleteBySourceIDs(ctx, "wiki", wikiDeleteIDs)
|
||||
_ = ragRepo.DeleteBySourceIDs(ctx, "entity", entityDeleteIDs)
|
||||
|
||||
for _, wiki := range snapshotData.Wikis {
|
||||
if wiki.Source == "inline" {
|
||||
cleanText := s.ragUtils.StripHTML(wiki.Title + "\n" + wiki.Doc)
|
||||
chunks, vectors, err := s.ragUtils.PrepareChunks(ctx, cleanText)
|
||||
if err == nil {
|
||||
_ = ragRepo.DeleteBySourceIDs(ctx, "wiki", []string{wiki.ID})
|
||||
for i, chunk := range chunks {
|
||||
_ = ragRepo.SaveChunk(ctx, "wiki", wiki.ID, commit.ProjectID, i, chunk, vectors[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, entity := range snapshotData.Entities {
|
||||
if entity.Source == "inline" {
|
||||
cleanText := s.ragUtils.StripHTML(entity.Name + "\n" + entity.Description)
|
||||
chunks, vectors, err := s.ragUtils.PrepareChunks(ctx, cleanText)
|
||||
if err == nil {
|
||||
_ = ragRepo.DeleteBySourceIDs(ctx, "entity", []string{entity.ID})
|
||||
for i, chunk := range chunks {
|
||||
_ = ragRepo.SaveChunk(ctx, "entity", entity.ID, commit.ProjectID, i, chunk, vectors[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arg := sqlc.UpdateSubmissionParams{
|
||||
ID: submissionUUID,
|
||||
Status: pgtype.Int2{Int16: status.Int16(), Valid: true},
|
||||
@@ -563,12 +624,12 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
|
||||
updatedSubmission, err := submissionRepo.Update(ctx, arg)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update submission status: " + err.Error())
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update submission status: "+err.Error())
|
||||
}
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction: " + err.Error())
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction: "+err.Error())
|
||||
}
|
||||
|
||||
if status == constants.StatusTypeApproved {
|
||||
@@ -579,6 +640,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
_ = s.c.DelByPattern(bgCtx, "wiki:search*")
|
||||
}()
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("project:id:%s", submission.ProjectID))
|
||||
|
||||
return updatedSubmission.ToResponse(), nil
|
||||
|
||||
Reference in New Issue
Block a user