Fix summarizer worker infinite loop and map AI trigger errors to safe HTTP responses.

Paginate un-summarized tenders and mark rows missing notice/folder IDs as handled so the worker cannot spin forever. Return 400/404 for validation and not-found cases on AI summarize/analyze triggers instead of leaking internal errors as 500.
This commit is contained in:
Mazyar
2026-06-08 20:24:02 +03:30
parent 7d383c36c3
commit d07e1c9cf0
4 changed files with 64 additions and 13 deletions
+23 -10
View File
@@ -15,6 +15,7 @@ import (
"tm/internal/customer"
"tm/pkg/ai_summarizer"
"tm/pkg/logger"
orm "tm/pkg/mongo"
"tm/pkg/response"
)
@@ -1187,11 +1188,11 @@ func (s *tenderService) GetAISummary(ctx context.Context, id string) (*AISummary
// It calls the external AI service HTTP API to generate summaries.
func (s *tenderService) TriggerAISummarize(ctx context.Context, id string) (*AISummarizeResponse, error) {
if id == "" {
return nil, fmt.Errorf("tender ID cannot be empty")
return nil, ErrEmptyTenderID
}
if s.aiSummarizerClient == nil {
return nil, fmt.Errorf("AI summarizer service is not configured")
return nil, ErrAINotConfigured
}
// Get tender to build the request
@@ -1201,18 +1202,24 @@ func (s *tenderService) TriggerAISummarize(ctx context.Context, id string) (*AIS
"tender_id": id,
"error": err.Error(),
})
if errors.Is(err, orm.ErrDocumentNotFound) {
return nil, ErrTenderNotFound
}
if strings.Contains(err.Error(), "invalid id format") {
return nil, ErrInvalidTenderID
}
return nil, fmt.Errorf("failed to get tender: %w", err)
}
if t == nil {
return nil, fmt.Errorf("tender not found")
return nil, ErrTenderNotFound
}
if t.NoticePublicationID == "" {
return nil, fmt.Errorf("tender has no notice publication ID")
return nil, ErrTenderMissingNoticeID
}
if strings.TrimSpace(t.ContractFolderID) == "" {
return nil, fmt.Errorf("tender has no contract folder ID")
return nil, ErrTenderMissingContractFolderID
}
reqBody := ai_summarizer.NewSummarizeRequest(t.ContractFolderID, t.NoticePublicationID)
@@ -1274,10 +1281,10 @@ func (s *tenderService) TriggerAISummarize(ctx context.Context, id string) (*AIS
// TriggerAIAnalyze triggers on-demand agentic analysis for a tender.
func (s *tenderService) TriggerAIAnalyze(ctx context.Context, id string) (*AIAnalyzeResponse, error) {
if id == "" {
return nil, fmt.Errorf("tender ID cannot be empty")
return nil, ErrEmptyTenderID
}
if s.aiSummarizerClient == nil {
return nil, fmt.Errorf("AI summarizer service is not configured")
return nil, ErrAINotConfigured
}
t, err := s.repository.GetByID(ctx, id)
@@ -1286,16 +1293,22 @@ func (s *tenderService) TriggerAIAnalyze(ctx context.Context, id string) (*AIAna
"tender_id": id,
"error": err.Error(),
})
if errors.Is(err, orm.ErrDocumentNotFound) {
return nil, ErrTenderNotFound
}
if strings.Contains(err.Error(), "invalid id format") {
return nil, ErrInvalidTenderID
}
return nil, fmt.Errorf("failed to get tender: %w", err)
}
if t == nil {
return nil, fmt.Errorf("tender not found")
return nil, ErrTenderNotFound
}
if t.NoticePublicationID == "" {
return nil, fmt.Errorf("tender has no notice publication ID")
return nil, ErrTenderMissingNoticeID
}
if strings.TrimSpace(t.ContractFolderID) == "" {
return nil, fmt.Errorf("tender has no contract folder ID")
return nil, ErrTenderMissingContractFolderID
}
reqBody := ai_summarizer.NewAnalyzeRequest(t.ContractFolderID, t.NoticePublicationID)