From 0fbcc7437793fb4ddd0091435ea251c78e0dfd0a Mon Sep 17 00:00:00 2001 From: Mazyar Date: Sat, 16 May 2026 17:44:27 +0330 Subject: [PATCH] overall summary respose --- internal/tender/form.go | 2 ++ internal/tender/service.go | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/internal/tender/form.go b/internal/tender/form.go index e097dd9..d9508a2 100644 --- a/internal/tender/form.go +++ b/internal/tender/form.go @@ -319,6 +319,8 @@ func (t *Tender) ToResponseWithLanguage(language string) *TenderResponse { ProjectName: strings.TrimSpace(t.ProjectName), CreatedAt: t.CreatedAt, Language: usedLanguage, + + OverallSummary: strings.TrimSpace(t.AIOverallSummary), } return response diff --git a/internal/tender/service.go b/internal/tender/service.go index de42e7a..98a6900 100644 --- a/internal/tender/service.go +++ b/internal/tender/service.go @@ -123,20 +123,19 @@ func (s *tenderService) GetByID(ctx context.Context, id string) (*TenderResponse return resp, nil } -// enrichWithAISummary attempts to fetch the AI-generated overall summary from -// MinIO storage and attaches it to the response. Failures are logged but do not -// cause the request to fail — the field will simply be omitted. -// -// Because one tender record can span several TED notices (we merge by -// 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. +// enrichWithAISummary loads overall_summary from MinIO (PROC_//tender.json) +// and attaches it to the response. The primary notice publication id is tried first, then related ids. +// 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 +// another MinIO round-trip. 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 } - // Keep tender detail response fast even if AI storage is slow/unreachable. enrichCtx, cancel := context.WithTimeout(ctx, aiSummaryEnrichmentTimeout) defer cancel() @@ -160,6 +159,25 @@ func (s *tenderService) enrichWithAISummary(ctx context.Context, t *Tender, resp "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