diff --git a/pkg/ai_summarizer/entities.go b/pkg/ai_summarizer/entities.go index f114936..4431885 100644 --- a/pkg/ai_summarizer/entities.go +++ b/pkg/ai_summarizer/entities.go @@ -1,5 +1,7 @@ package ai_summarizer +import "strings" + // SummarizeRequest represents the request payload sent to the AI service // POST /ai/summarize endpoint for on-demand summarization. type SummarizeRequest struct { @@ -61,11 +63,30 @@ type TranslateResponse struct { // TenderJSON represents the structure of the /tender.json file // stored in the AI service's MinIO bucket. type TenderJSON struct { - OverallSummary *string `json:"overall_summary"` // nil if not yet generated - SummarizeStatus SummarizeStatus `json:"summarize_status"` + OverallSummary *string `json:"overall_summary"` + OverallSummaryCamel *string `json:"overallSummary"` + SummarizeStatus SummarizeStatus `json:"summarize_status"` + SummarizeStatusCamel SummarizeStatus `json:"summarizeStatus"` +} + +// resolved returns whether summarization is marked done and the best-effort +// overall summary pointer (may be nil or empty when the pipeline finished +// without text yet). +func (t *TenderJSON) resolved() (done bool, overall *string) { + done = t.SummarizeStatus.isDone() || t.SummarizeStatusCamel.isDone() + overall = t.OverallSummary + if overall == nil || strings.TrimSpace(*overall) == "" { + overall = t.OverallSummaryCamel + } + return done, overall } // SummarizeStatus represents the summarization status inside tender.json. type SummarizeStatus struct { - Done bool `json:"done"` // true when the pipeline has finished + Done bool `json:"done"` // true when the pipeline has finished + DoneUpper bool `json:"Done"` // some writers emit PascalCase +} + +func (s SummarizeStatus) isDone() bool { + return s.Done || s.DoneUpper } diff --git a/pkg/ai_summarizer/storage.go b/pkg/ai_summarizer/storage.go index d946d3d..1eda914 100644 --- a/pkg/ai_summarizer/storage.go +++ b/pkg/ai_summarizer/storage.go @@ -160,22 +160,24 @@ func (s *StorageClient) getSummaryFromObjectKey(ctx context.Context, objectKey, return "", fmt.Errorf("ai_summarizer: failed to parse tender.json: %w", err) } - if !tenderJSON.SummarizeStatus.Done { + done, overall := tenderJSON.resolved() + if !done { return "", ErrSummaryNotReady } - - if tenderJSON.OverallSummary == nil || *tenderJSON.OverallSummary == "" { + if overall == nil || strings.TrimSpace(*overall) == "" { return "", ErrNoOverallSummary } + summaryText := strings.TrimSpace(*overall) + s.logger.Info("Retrieved overall_summary from storage", map[string]interface{}{ "contract_folder_id": contractFolderID, "notice_id": noticePublicationID, "object_key": objectKey, - "summary_length": len(*tenderJSON.OverallSummary), + "summary_length": len(summaryText), }) - return *tenderJSON.OverallSummary, nil + return summaryText, nil } // IsSummaryReady is a convenience method that checks whether the tender.json @@ -207,7 +209,8 @@ func (s *StorageClient) IsSummaryReady(ctx context.Context, contractFolderID, no return false, fmt.Errorf("ai_summarizer: failed to parse tender.json: %w", err) } - return tenderJSON.SummarizeStatus.Done, nil + done, _ := tenderJSON.resolved() + return done, nil } // ListDocuments lists document files stored under @@ -351,19 +354,28 @@ func (s *StorageClient) GetAllSummaries(ctx context.Context) ([]TenderSummaryIte summary, err = s.getSummaryFromObjectKey(ctx, key, contractFolderID, noticePublicationID) } if err != nil { - s.logger.Debug("Skipping tender summary", map[string]interface{}{ - "object_key": key, - "contract_folder_id": contractFolderID, - "notice_id": noticePublicationID, - "error": err.Error(), - }) - - if errors.Is(err, ErrSummaryNotReady) { + switch { + case errors.Is(err, ErrSummaryNotReady): results = append(results, TenderSummaryItem{ ContractFolderID: contractFolderID, NoticeID: noticePublicationID, Done: false, }) + case errors.Is(err, ErrNoOverallSummary): + // File exists and pipeline reports done, but no summary text yet (or empty). + results = append(results, TenderSummaryItem{ + ContractFolderID: contractFolderID, + NoticeID: noticePublicationID, + OverallSummary: "", + Done: true, + }) + default: + s.logger.Debug("Skipping tender summary", map[string]interface{}{ + "object_key": key, + "contract_folder_id": contractFolderID, + "notice_id": noticePublicationID, + "error": err.Error(), + }) } continue }