feat: implement ChatbotService for RAG-based AI interactions and history management
Build and Release / release (push) Successful in 1m17s

This commit is contained in:
2026-06-04 07:50:25 +07:00
parent e061b9da05
commit 9654c059d5
+24 -11
View File
@@ -2,7 +2,6 @@ package services
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"history-api/internal/dtos/request" "history-api/internal/dtos/request"
"history-api/internal/gen/sqlc" "history-api/internal/gen/sqlc"
@@ -11,8 +10,10 @@ import (
"history-api/pkg/ai" "history-api/pkg/ai"
"history-api/pkg/constants" "history-api/pkg/constants"
"history-api/pkg/convert" "history-api/pkg/convert"
"strings"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/rs/zerolog/log"
) )
type ChatbotService interface { type ChatbotService interface {
@@ -43,22 +44,31 @@ func (s *chatbotService) Chat(ctx context.Context, userID string, projectID *str
} }
if usage >= constants.MaxDailyAIUsage { if usage >= constants.MaxDailyAIUsage {
return "", errors.New("you have reached your daily limit of 10 questions. Please come back tomorrow") return "", fmt.Errorf("you have reached your daily limit of %d questions. Please come back tomorrow", constants.MaxDailyAIUsage)
} }
qVector, err := s.ragUtils.EmbedQuery(ctx, question) qVector, err := s.ragUtils.EmbedQuery(ctx, question)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to embed question: %w", err) return "", fmt.Errorf("failed to embed question: %w", err)
} }
results, err := s.repo.SearchSimilar(ctx, projectID, qVector, 5, 0.65)
results, err := s.repo.SearchSimilar(ctx, projectID, qVector, 8, 0.50)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to search similar content: %w", err) return "", fmt.Errorf("failed to search similar content: %w", err)
} }
contextStr := "" if len(results) < 3 {
for i, res := range results { broadResults, err := s.repo.SearchSimilar(ctx, projectID, qVector, 8, 0.35)
contextStr += fmt.Sprintf("[Document %d]: %s\n", i+1, res.Content) if err == nil && len(broadResults) > len(results) {
results = broadResults
} }
}
var contextBuilder strings.Builder
for i, res := range results {
contextBuilder.WriteString(fmt.Sprintf("[Document %d (score: %.2f)]: %s\n", i+1, res.Similarity, res.Content))
}
contextStr := contextBuilder.String()
pgUserID, err := convert.StringToUUID(userID) pgUserID, err := convert.StringToUUID(userID)
if err != nil { if err != nil {
@@ -70,13 +80,14 @@ func (s *chatbotService) Chat(ctx context.Context, userID string, projectID *str
Limit: 10, Limit: 10,
}) })
if err != nil { if err != nil {
fmt.Printf("Warning: failed to get chatbot history: %v\n", err) log.Warn().Err(err).Msg("failed to get chatbot history")
} }
historyStr := "" var historyBuilder strings.Builder
for _, h := range histories { for _, h := range histories {
historyStr += fmt.Sprintf("User: %s\nAssistant: %s\n\n", h.Question, h.Answer) historyBuilder.WriteString(fmt.Sprintf("User: %s\nAssistant: %s\n\n", h.Question, h.Answer))
} }
historyStr := historyBuilder.String()
var prompt string var prompt string
if contextStr == "" { if contextStr == "" {
@@ -115,7 +126,9 @@ Question: %s`, contextStr, historyStr, question)
if err != nil { if err != nil {
return "", err return "", err
} }
_, _ = s.usageRepo.IncrementAIUsage(ctx, userID) if _, err := s.usageRepo.IncrementAIUsage(ctx, userID); err != nil {
log.Warn().Err(err).Str("userID", userID).Msg("failed to increment AI usage")
}
_, err = s.chatRepo.CreateChatbotHistory(ctx, sqlc.CreateChatbotHistoryParams{ _, err = s.chatRepo.CreateChatbotHistory(ctx, sqlc.CreateChatbotHistoryParams{
UserID: pgUserID, UserID: pgUserID,
@@ -123,7 +136,7 @@ Question: %s`, contextStr, historyStr, question)
Answer: response, Answer: response,
}) })
if err != nil { if err != nil {
fmt.Printf("Warning: failed to save chatbot history: %v\n", err) log.Warn().Err(err).Msg("failed to save chatbot history")
} }
return response, nil return response, nil