tender summary response fix
This commit is contained in:
@@ -166,25 +166,25 @@ func (w *DocumentSummarizationWorker) summarizeDocumentViaAI(ctx context.Context
|
|||||||
return "", "", err.Error()
|
return "", "", err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
noticeID := strings.TrimSpace(t.NoticePublicationID)
|
if strings.TrimSpace(t.NoticePublicationID) == "" {
|
||||||
if noticeID == "" {
|
|
||||||
return "", "", "tender has no notice_publication_id for AI summarize request"
|
return "", "", "tender has no notice_publication_id for AI summarize request"
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(t.ContractFolderID) == "" {
|
||||||
|
return "", "", "tender has no contract_folder_id for AI summarize request"
|
||||||
|
}
|
||||||
|
|
||||||
req := ai_summarizer.SummarizeRequest{
|
req := ai_summarizer.NewSummarizeRequest(ai_summarizer.SummarizeRequestInput{
|
||||||
NoticeID: noticeID,
|
ContractFolderID: t.ContractFolderID,
|
||||||
DocumentURL: docURL,
|
NoticePublicationID: t.NoticePublicationID,
|
||||||
Title: t.Title,
|
DocumentURL: docURL,
|
||||||
Description: t.Description,
|
Title: t.Title,
|
||||||
Country: t.CountryCode,
|
Description: t.Description,
|
||||||
Currency: t.Currency,
|
Country: t.CountryCode,
|
||||||
SubmissionDeadline: "",
|
Currency: t.Currency,
|
||||||
AuthorityName: authorityNameFromTender(t),
|
SubmissionDeadline: t.SubmissionDeadline,
|
||||||
}
|
EstimatedValue: t.EstimatedValue,
|
||||||
if t.EstimatedValue > 0 {
|
AuthorityName: authorityNameFromTender(t),
|
||||||
v := t.EstimatedValue
|
})
|
||||||
req.EstimatedValue = &v
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := w.AIClient.FetchSummaryOnDemand(ctx, req)
|
resp, err := w.AIClient.FetchSummaryOnDemand(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -535,9 +535,10 @@ type AISummaryResponse struct {
|
|||||||
|
|
||||||
// AISummarizeResponse represents the response for triggering on-demand AI summarization
|
// AISummarizeResponse represents the response for triggering on-demand AI summarization
|
||||||
type AISummarizeResponse struct {
|
type AISummarizeResponse struct {
|
||||||
NoticeID string `json:"notice_id"`
|
ContractFolderID string `json:"contract_folder_id"`
|
||||||
Summaries []AIDocumentSummary `json:"summaries"`
|
NoticePublicationID string `json:"notice_publication_id"`
|
||||||
OverallSummary *string `json:"overall_summary"`
|
Summaries []AIDocumentSummary `json:"summaries"`
|
||||||
|
OverallSummary *string `json:"overall_summary"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AIDocumentSummary represents a single document summary from the AI service
|
// AIDocumentSummary represents a single document summary from the AI service
|
||||||
|
|||||||
+42
-30
@@ -952,44 +952,45 @@ func (s *tenderService) TriggerAISummarize(ctx context.Context, id string) (*AIS
|
|||||||
return nil, fmt.Errorf("tender not found")
|
return nil, fmt.Errorf("tender not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
noticeID := t.NoticePublicationID
|
if t.NoticePublicationID == "" {
|
||||||
if noticeID == "" {
|
|
||||||
return nil, fmt.Errorf("tender has no notice publication ID")
|
return nil, fmt.Errorf("tender has no notice publication ID")
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(t.ContractFolderID) == "" {
|
||||||
// Build the AI summarizer request
|
return nil, fmt.Errorf("tender has no contract folder ID")
|
||||||
reqBody := ai_summarizer.SummarizeRequest{
|
|
||||||
NoticeID: noticeID,
|
|
||||||
DocumentURL: t.DocumentURI,
|
|
||||||
Title: t.Title,
|
|
||||||
Description: t.Description,
|
|
||||||
Country: t.CountryCode,
|
|
||||||
SubmissionDeadline: time.UnixMilli(t.SubmissionDeadline).Format(time.RFC3339),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.EstimatedValue > 0 {
|
authorityName := ""
|
||||||
ev := t.EstimatedValue
|
|
||||||
reqBody.EstimatedValue = &ev
|
|
||||||
}
|
|
||||||
if t.Currency != "" {
|
|
||||||
reqBody.Currency = t.Currency
|
|
||||||
}
|
|
||||||
if t.BuyerOrganization != nil {
|
if t.BuyerOrganization != nil {
|
||||||
reqBody.AuthorityName = t.BuyerOrganization.Name
|
authorityName = t.BuyerOrganization.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reqBody := ai_summarizer.NewSummarizeRequest(ai_summarizer.SummarizeRequestInput{
|
||||||
|
ContractFolderID: t.ContractFolderID,
|
||||||
|
NoticePublicationID: t.NoticePublicationID,
|
||||||
|
DocumentURL: t.DocumentURI,
|
||||||
|
Title: t.Title,
|
||||||
|
Description: t.Description,
|
||||||
|
Country: t.CountryCode,
|
||||||
|
Currency: t.Currency,
|
||||||
|
SubmissionDeadline: t.SubmissionDeadline,
|
||||||
|
EstimatedValue: t.EstimatedValue,
|
||||||
|
AuthorityName: authorityName,
|
||||||
|
})
|
||||||
|
|
||||||
s.logger.Info("Triggering on-demand AI summarization", map[string]interface{}{
|
s.logger.Info("Triggering on-demand AI summarization", map[string]interface{}{
|
||||||
"tender_id": id,
|
"tender_id": id,
|
||||||
"notice_id": noticeID,
|
"contract_folder_id": t.ContractFolderID,
|
||||||
|
"notice_publication_id": t.NoticePublicationID,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Call the AI service
|
// Call the AI service
|
||||||
resp, err := s.aiSummarizerClient.FetchSummaryOnDemand(ctx, reqBody)
|
resp, err := s.aiSummarizerClient.FetchSummaryOnDemand(ctx, reqBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("On-demand AI summarization failed", map[string]interface{}{
|
s.logger.Error("On-demand AI summarization failed", map[string]interface{}{
|
||||||
"tender_id": id,
|
"tender_id": id,
|
||||||
"notice_id": noticeID,
|
"contract_folder_id": t.ContractFolderID,
|
||||||
"error": err.Error(),
|
"notice_publication_id": t.NoticePublicationID,
|
||||||
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
return nil, fmt.Errorf("AI summarization request failed: %w", err)
|
return nil, fmt.Errorf("AI summarization request failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -1007,15 +1008,26 @@ func (s *tenderService) TriggerAISummarize(ctx context.Context, id string) (*AIS
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("On-demand AI summarization completed", map[string]interface{}{
|
s.logger.Info("On-demand AI summarization completed", map[string]interface{}{
|
||||||
"tender_id": id,
|
"tender_id": id,
|
||||||
"notice_id": noticeID,
|
"contract_folder_id": t.ContractFolderID,
|
||||||
"summaries_count": len(summaries),
|
"notice_publication_id": t.NoticePublicationID,
|
||||||
|
"summaries_count": len(summaries),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
noticePublicationID := resp.ResolvedNoticePublicationID()
|
||||||
|
if noticePublicationID == "" {
|
||||||
|
noticePublicationID = t.NoticePublicationID
|
||||||
|
}
|
||||||
|
contractFolderID := strings.TrimSpace(resp.ContractFolderID)
|
||||||
|
if contractFolderID == "" {
|
||||||
|
contractFolderID = t.ContractFolderID
|
||||||
|
}
|
||||||
|
|
||||||
return &AISummarizeResponse{
|
return &AISummarizeResponse{
|
||||||
NoticeID: resp.NoticeID,
|
ContractFolderID: contractFolderID,
|
||||||
Summaries: summaries,
|
NoticePublicationID: noticePublicationID,
|
||||||
OverallSummary: resp.OverallSummary,
|
Summaries: summaries,
|
||||||
|
OverallSummary: resp.OverallSummary,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,9 @@ func (c *Client) FetchSummaryOnDemand(ctx context.Context, reqBody SummarizeRequ
|
|||||||
for attempt := 0; attempt <= c.config.APIRetryCount; attempt++ {
|
for attempt := 0; attempt <= c.config.APIRetryCount; attempt++ {
|
||||||
if attempt > 0 {
|
if attempt > 0 {
|
||||||
c.logger.Warn("Retrying AI summarize request", map[string]interface{}{
|
c.logger.Warn("Retrying AI summarize request", map[string]interface{}{
|
||||||
"notice_id": reqBody.NoticeID,
|
"contract_folder_id": reqBody.ContractFolderID,
|
||||||
"attempt": attempt,
|
"notice_publication_id": reqBody.NoticePublicationID,
|
||||||
|
"attempt": attempt,
|
||||||
})
|
})
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@@ -67,9 +68,10 @@ func (c *Client) FetchSummaryOnDemand(ctx context.Context, reqBody SummarizeRequ
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
lastErr = err
|
lastErr = err
|
||||||
c.logger.Error("AI summarize request failed", map[string]interface{}{
|
c.logger.Error("AI summarize request failed", map[string]interface{}{
|
||||||
"notice_id": reqBody.NoticeID,
|
"contract_folder_id": reqBody.ContractFolderID,
|
||||||
"attempt": attempt,
|
"notice_publication_id": reqBody.NoticePublicationID,
|
||||||
"error": err.Error(),
|
"attempt": attempt,
|
||||||
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -205,8 +207,9 @@ func (c *Client) doPost(ctx context.Context, url string, jsonBody []byte) (*Summ
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Info("AI summarize request succeeded", map[string]interface{}{
|
c.logger.Info("AI summarize request succeeded", map[string]interface{}{
|
||||||
"notice_id": result.NoticeID,
|
"contract_folder_id": result.ContractFolderID,
|
||||||
"summaries_count": len(result.Summaries),
|
"notice_publication_id": result.ResolvedNoticePublicationID(),
|
||||||
|
"summaries_count": len(result.Summaries),
|
||||||
})
|
})
|
||||||
|
|
||||||
return &result, nil
|
return &result, nil
|
||||||
|
|||||||
@@ -1,26 +1,79 @@
|
|||||||
package ai_summarizer
|
package ai_summarizer
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// SummarizeRequest represents the request payload sent to the AI service
|
// SummarizeRequest represents the request payload sent to the AI service
|
||||||
// POST /ai/summarize endpoint for on-demand summarization.
|
// POST /ai/summarize endpoint for on-demand summarization.
|
||||||
type SummarizeRequest struct {
|
type SummarizeRequest struct {
|
||||||
NoticeID string `json:"notice_id"` // required
|
ContractFolderID string `json:"contract_folder_id"`
|
||||||
DocumentURL string `json:"document_url"` // required
|
NoticePublicationID string `json:"notice_publication_id"`
|
||||||
Title string `json:"title"` // required
|
DocumentURL string `json:"document_url,omitempty"`
|
||||||
Description string `json:"description"` // required
|
Title string `json:"title,omitempty"`
|
||||||
Country string `json:"country,omitempty"` // optional
|
Description string `json:"description,omitempty"`
|
||||||
EstimatedValue *float64 `json:"estimated_value,omitempty"` // optional (pointer to distinguish 0 from absent)
|
Country string `json:"country,omitempty"`
|
||||||
Currency string `json:"currency,omitempty"` // optional
|
EstimatedValue *float64 `json:"estimated_value,omitempty"`
|
||||||
SubmissionDeadline string `json:"submission_deadline,omitempty"` // optional
|
Currency string `json:"currency,omitempty"`
|
||||||
AuthorityName string `json:"authority_name,omitempty"` // optional
|
SubmissionDeadline string `json:"submission_deadline,omitempty"`
|
||||||
|
AuthorityName string `json:"authority_name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SummarizeRequestInput collects fields used to build SummarizeRequest.
|
||||||
|
type SummarizeRequestInput struct {
|
||||||
|
ContractFolderID string
|
||||||
|
NoticePublicationID string
|
||||||
|
DocumentURL string
|
||||||
|
Title string
|
||||||
|
Description string
|
||||||
|
Country string
|
||||||
|
Currency string
|
||||||
|
SubmissionDeadline int64 // Unix seconds; omitted from request when zero
|
||||||
|
EstimatedValue float64
|
||||||
|
AuthorityName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSummarizeRequest builds a SummarizeRequest for POST /ai/summarize.
|
||||||
|
func NewSummarizeRequest(in SummarizeRequestInput) SummarizeRequest {
|
||||||
|
req := SummarizeRequest{
|
||||||
|
ContractFolderID: strings.TrimSpace(in.ContractFolderID),
|
||||||
|
NoticePublicationID: strings.TrimSpace(in.NoticePublicationID),
|
||||||
|
DocumentURL: strings.TrimSpace(in.DocumentURL),
|
||||||
|
Title: in.Title,
|
||||||
|
Description: in.Description,
|
||||||
|
Country: in.Country,
|
||||||
|
Currency: in.Currency,
|
||||||
|
AuthorityName: strings.TrimSpace(in.AuthorityName),
|
||||||
|
}
|
||||||
|
if in.EstimatedValue > 0 {
|
||||||
|
v := in.EstimatedValue
|
||||||
|
req.EstimatedValue = &v
|
||||||
|
}
|
||||||
|
if in.SubmissionDeadline > 0 {
|
||||||
|
req.SubmissionDeadline = time.Unix(in.SubmissionDeadline, 0).UTC().Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
// SummarizeResponse represents the response from the AI summarization API.
|
// SummarizeResponse represents the response from the AI summarization API.
|
||||||
type SummarizeResponse struct {
|
type SummarizeResponse struct {
|
||||||
NoticeID string `json:"notice_id"`
|
ContractFolderID string `json:"contract_folder_id"`
|
||||||
Summaries []DocumentSummary `json:"summaries"`
|
NoticePublicationID string `json:"notice_publication_id"`
|
||||||
OverallSummary *string `json:"overall_summary"` // nil when not available
|
NoticeID string `json:"notice_id"` // legacy field name
|
||||||
|
Summaries []DocumentSummary `json:"summaries"`
|
||||||
|
OverallSummary *string `json:"overall_summary"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolvedNoticePublicationID returns the notice publication ID from the response.
|
||||||
|
func (r *SummarizeResponse) ResolvedNoticePublicationID() string {
|
||||||
|
if r == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if id := strings.TrimSpace(r.NoticePublicationID); id != "" {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(r.NoticeID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DocumentSummary represents a summary for a single document within the AI response.
|
// DocumentSummary represents a summary for a single document within the AI response.
|
||||||
|
|||||||
Reference in New Issue
Block a user