overall summary respose

This commit is contained in:
Mazyar
2026-05-16 17:44:27 +03:30
parent 70f581da09
commit 0fbcc74377
2 changed files with 30 additions and 10 deletions
+2
View File
@@ -319,6 +319,8 @@ func (t *Tender) ToResponseWithLanguage(language string) *TenderResponse {
ProjectName: strings.TrimSpace(t.ProjectName), ProjectName: strings.TrimSpace(t.ProjectName),
CreatedAt: t.CreatedAt, CreatedAt: t.CreatedAt,
Language: usedLanguage, Language: usedLanguage,
OverallSummary: strings.TrimSpace(t.AIOverallSummary),
} }
return response return response
+28 -10
View File
@@ -123,20 +123,19 @@ func (s *tenderService) GetByID(ctx context.Context, id string) (*TenderResponse
return resp, nil return resp, nil
} }
// enrichWithAISummary attempts to fetch the AI-generated overall summary from // enrichWithAISummary loads overall_summary from MinIO (PROC_<contractFolderID>/<noticePublicationID>/tender.json)
// MinIO storage and attaches it to the response. Failures are logged but do not // and attaches it to the response. The primary notice publication id is tried first, then related ids.
// cause the request to fail — the field will simply be omitted. // A previously stored ai_overall_summary on the tender (already mapped in ToResponse) is kept when MinIO
// // has no ready summary. On success the tender document is updated so list/search can return it without
// Because one tender record can span several TED notices (we merge by // another MinIO round-trip.
// contract_folder_id), we try the latest publication id first and fall back to
// older RelatedNoticePublicationIDs if the AI pipeline only finished an
// earlier notice in the procedure.
func (s *tenderService) enrichWithAISummary(ctx context.Context, t *Tender, resp *TenderResponse) { func (s *tenderService) enrichWithAISummary(ctx context.Context, t *Tender, resp *TenderResponse) {
if s.aiSummarizerStorage == nil || t == nil || t.ContractFolderID == "" { if t == nil || resp == nil {
return
}
if s.aiSummarizerStorage == nil || strings.TrimSpace(t.ContractFolderID) == "" {
return return
} }
// Keep tender detail response fast even if AI storage is slow/unreachable.
enrichCtx, cancel := context.WithTimeout(ctx, aiSummaryEnrichmentTimeout) enrichCtx, cancel := context.WithTimeout(ctx, aiSummaryEnrichmentTimeout)
defer cancel() defer cancel()
@@ -160,6 +159,25 @@ func (s *tenderService) enrichWithAISummary(ctx context.Context, t *Tender, resp
"used_notice_id": usedNotice, "used_notice_id": usedNotice,
}) })
} }
s.persistAIOverallSummary(enrichCtx, t, summary)
}
// persistAIOverallSummary stores the resolved summary on the tender for list/search responses.
func (s *tenderService) persistAIOverallSummary(ctx context.Context, t *Tender, summary string) {
summary = strings.TrimSpace(summary)
if summary == "" || summary == strings.TrimSpace(t.AIOverallSummary) {
return
}
t.AIOverallSummary = summary
t.UpdatedAt = time.Now().Unix()
if err := s.repository.Update(ctx, t); err != nil {
s.logger.Warn("Failed to persist AI overall summary on tender", map[string]interface{}{
"tender_id": t.ID.Hex(),
"error": err.Error(),
})
}
} }
// lookupSummaryForTender returns the overall_summary for a tender by trying its // lookupSummaryForTender returns the overall_summary for a tender by trying its