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 { NoticeID string `json:"notice_id"` // required DocumentURL string `json:"document_url"` // required Title string `json:"title"` // required Description string `json:"description"` // required Country string `json:"country,omitempty"` // optional EstimatedValue *float64 `json:"estimated_value,omitempty"` // optional (pointer to distinguish 0 from absent) Currency string `json:"currency,omitempty"` // optional SubmissionDeadline string `json:"submission_deadline,omitempty"` // optional AuthorityName string `json:"authority_name,omitempty"` // optional } // SummarizeResponse represents the response from the AI summarization API. type SummarizeResponse struct { NoticeID string `json:"notice_id"` Summaries []DocumentSummary `json:"summaries"` OverallSummary *string `json:"overall_summary"` // nil when not available } // DocumentSummary represents a summary for a single document within the AI response. type DocumentSummary struct { DocumentName string `json:"document_name"` DocumentType string `json:"document_type"` Summary string `json:"summary"` SummaryModel string `json:"summary_model"` Error string `json:"error"` // empty string when no error } // StoredDocument represents a document object stored for a tender in MinIO. type StoredDocument struct { ObjectName string `json:"-"` DocumentName string `json:"document_name"` Size int64 `json:"size"` LastModified int64 `json:"last_modified"` DocumentType string `json:"document_type,omitempty"` } // TranslateRequest represents the request payload sent to the AI service // POST /ai/translate endpoint. type TranslateRequest struct { ContractFolderID string `json:"contract_folder_id"` NoticePublicationID string `json:"notice_publication_id"` Title string `json:"title"` Description string `json:"description"` Language string `json:"language"` } // TranslateResponse represents the translation payload from POST /ai/translate. type TranslateResponse struct { ContractFolderID string `json:"contract_folder_id"` NoticePublicationID string `json:"notice_publication_id"` Language string `json:"language"` TranslatedTitle string `json:"translated_title"` TranslatedDescription string `json:"translated_description"` } // PipelineTranslateRequest represents the request payload for POST /pipeline/translate. type PipelineTranslateRequest struct { Languages []string `json:"languages"` } // PipelineTranslateResponse represents the response from POST /pipeline/translate. type PipelineTranslateResponse struct { Status string `json:"status"` Languages []string `json:"languages"` } // StoredTranslation is one language entry inside tender.json translations map. type StoredTranslation struct { Title string `json:"title"` Description string `json:"description"` } // TranslationStatus tracks which languages the async pipeline has finished. type TranslationStatus struct { Done []string `json:"done"` DoneUpper []string `json:"Done"` } func (s TranslationStatus) languagesDone() []string { if len(s.Done) > 0 { return s.Done } return s.DoneUpper } func languageInDone(done []string, language string) bool { lang := strings.ToLower(strings.TrimSpace(language)) for _, item := range done { if strings.ToLower(strings.TrimSpace(item)) == lang { return true } } return false } // 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"` OverallSummaryCamel *string `json:"overallSummary"` SummarizeStatus SummarizeStatus `json:"summarize_status"` SummarizeStatusCamel SummarizeStatus `json:"summarizeStatus"` Translations map[string]StoredTranslation `json:"translations"` TranslationStatus TranslationStatus `json:"translation_status"` TranslationStatusCamel TranslationStatus `json:"translationStatus"` } // 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 DoneUpper bool `json:"Done"` // some writers emit PascalCase } func (s SummarizeStatus) isDone() bool { return s.Done || s.DoneUpper } // translationsDone returns languages marked complete by the translation pipeline. func (t *TenderJSON) translationsDone() []string { done := t.TranslationStatus.languagesDone() if len(done) > 0 { return done } return t.TranslationStatusCamel.languagesDone() } // translationForLanguage returns stored translation text when the pipeline marked // the language done and non-empty title exists. func (t *TenderJSON) translationForLanguage(language string) (StoredTranslation, bool) { if t == nil || !languageInDone(t.translationsDone(), language) { return StoredTranslation{}, false } lang := strings.ToLower(strings.TrimSpace(language)) if t.Translations != nil { for key, entry := range t.Translations { if strings.ToLower(strings.TrimSpace(key)) != lang { continue } if strings.TrimSpace(entry.Title) == "" { continue } return entry, true } } return StoredTranslation{}, false }