feat: implement RAG chatbot service, background cron worker, and asynchronous indexing infrastructure
All checks were successful
Build and Release / release (push) Successful in 1m30s

This commit is contained in:
2026-05-07 11:38:18 +07:00
parent 76bb1735bb
commit a4fed88b8a
7 changed files with 0 additions and 13 deletions

View File

@@ -35,7 +35,6 @@ func runBackup(ctx context.Context, s3 storage.Storage, dbURI string) {
fileName := fmt.Sprintf("db_backup_%s.sql", time.Now().Format("2006-01-02_15-04-05")) fileName := fmt.Sprintf("db_backup_%s.sql", time.Now().Format("2006-01-02_15-04-05"))
filePath := filepath.Join(tmpDir, fileName) filePath := filepath.Join(tmpDir, fileName)
// Run pg_dump
cmd := exec.Command("pg_dump", dbURI, "-F", "c", "-f", filePath) cmd := exec.Command("pg_dump", dbURI, "-F", "c", "-f", filePath)
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
log.Error().Err(err).Msg("Failed to execute pg_dump. Make sure pg_dump is installed.") log.Error().Err(err).Msg("Failed to execute pg_dump. Make sure pg_dump is installed.")

View File

@@ -40,7 +40,6 @@ func processRagTask(ctx context.Context, ragRepo repositories.RagRepository, rag
} }
} }
// 2. Index wikis with delay + retry
for _, wiki := range task.Wikis { for _, wiki := range task.Wikis {
if wiki.Source != "inline" { if wiki.Source != "inline" {
continue continue
@@ -83,7 +82,6 @@ func processRagTask(ctx context.Context, ragRepo repositories.RagRepository, rag
continue continue
} }
// Delete existing chunks then save new ones
_ = ragRepo.DeleteBySourceIDs(ctx, "wiki", []string{wiki.ID}) _ = ragRepo.DeleteBySourceIDs(ctx, "wiki", []string{wiki.ID})
for i, chunk := range chunks { for i, chunk := range chunks {
if saveErr := ragRepo.SaveChunk(ctx, "wiki", wiki.ID, task.ProjectID, i, chunk, vectors[i]); saveErr != nil { if saveErr := ragRepo.SaveChunk(ctx, "wiki", wiki.ID, task.ProjectID, i, chunk, vectors[i]); saveErr != nil {
@@ -92,12 +90,9 @@ func processRagTask(ctx context.Context, ragRepo repositories.RagRepository, rag
} }
log.Info().Str("worker", workerName).Str("wiki_id", wiki.ID).Int("chunks", len(chunks)).Msg("Wiki indexed successfully") log.Info().Str("worker", workerName).Str("wiki_id", wiki.ID).Int("chunks", len(chunks)).Msg("Wiki indexed successfully")
// Delay between items to avoid rate limit
time.Sleep(itemDelay) time.Sleep(itemDelay)
} }
// 3. Index entities with delay + retry
for _, entity := range task.Entities { for _, entity := range task.Entities {
if entity.Source != "inline" { if entity.Source != "inline" {
continue continue
@@ -140,7 +135,6 @@ func processRagTask(ctx context.Context, ragRepo repositories.RagRepository, rag
continue continue
} }
// Delete existing chunks then save new ones
_ = ragRepo.DeleteBySourceIDs(ctx, "entity", []string{entity.ID}) _ = ragRepo.DeleteBySourceIDs(ctx, "entity", []string{entity.ID})
for i, chunk := range chunks { for i, chunk := range chunks {
if saveErr := ragRepo.SaveChunk(ctx, "entity", entity.ID, task.ProjectID, i, chunk, vectors[i]); saveErr != nil { if saveErr := ragRepo.SaveChunk(ctx, "entity", entity.ID, task.ProjectID, i, chunk, vectors[i]); saveErr != nil {

View File

@@ -49,7 +49,6 @@ func (cx *ChatbotController) Chat(c fiber.Ctx) error {
answer, err := cx.chatbotService.Chat(ctx, claims.UId, dto.ProjectID, dto.Question) answer, err := cx.chatbotService.Chat(ctx, claims.UId, dto.ProjectID, dto.Question)
if err != nil { if err != nil {
// Trả về lỗi 429 (Too Many Requests) nếu hết lượt dùng
if err.Error() == "you have reached your daily limit of 10 questions. Please come back tomorrow" { if err.Error() == "you have reached your daily limit of 10 questions. Please come back tomorrow" {
return c.Status(fiber.StatusTooManyRequests).JSON(response.CommonResponse{ return c.Status(fiber.StatusTooManyRequests).JSON(response.CommonResponse{
Status: false, Status: false,

View File

@@ -61,7 +61,6 @@ func (r *rasterTileRepository) GetTile(ctx context.Context, z, x, y int) ([]byte
return nil, "", fmt.Errorf("invalid tile coordinates") return nil, "", fmt.Errorf("invalid tile coordinates")
} }
// cache key
cacheId := fmt.Sprintf("rasterTile:%d:%d:%d", z, x, y) cacheId := fmt.Sprintf("rasterTile:%d:%d:%d", z, x, y)
var cached []byte var cached []byte

View File

@@ -224,7 +224,6 @@ func (r *statisticRepository) Upsert(ctx context.Context, date time.Time) (*mode
entity := mapToEntity(row) entity := mapToEntity(row)
// Clear search cache and the specific date cache
go func() { go func() {
bgCtx := context.Background() bgCtx := context.Background()
_ = r.c.DelByPattern(bgCtx, "statistic:search*") _ = r.c.DelByPattern(bgCtx, "statistic:search*")

View File

@@ -61,7 +61,6 @@ func (r *tileRepository) GetTile(ctx context.Context, z, x, y int) ([]byte, stri
return nil, "", false, fmt.Errorf("invalid tile coordinates") return nil, "", false, fmt.Errorf("invalid tile coordinates")
} }
// cache key
cacheId := fmt.Sprintf("tile:%d:%d:%d", z, x, y) cacheId := fmt.Sprintf("tile:%d:%d:%d", z, x, y)
var cached []byte var cached []byte

View File

@@ -78,8 +78,6 @@ Question: %s`, contextStr, question)
if err != nil { if err != nil {
return "", err return "", err
} }
// 3. Tăng số lần sử dụng sau khi gọi AI thành công
_, _ = s.usageRepo.IncrementAIUsage(ctx, userID) _, _ = s.usageRepo.IncrementAIUsage(ctx, userID)
return response, nil return response, nil