Refactor AI summarizer client initialization to include MongoDB manager and add statistics endpoint
- Updated `InitAISummarizerClient` to accept `mongoManager` for tracking translation success. - Introduced new `Statistics` endpoint in the dashboard to fetch scraping and translation statistics. - Enhanced `TranslationWorker` to utilize the new success counter for tracking successful translations. - Added necessary data structures and query forms for statistics reporting. This refactor improves the tracking of AI translation success and provides new insights through the dashboard statistics.
This commit is contained in:
@@ -107,7 +107,7 @@ func (c *Client) FetchTranslationOnDemand(ctx context.Context, reqBody Translate
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := c.doTranslatePost(ctx, url, jsonBody)
|
||||
resp, err := c.doTranslatePost(ctx, url, jsonBody, reqBody.RequestSource)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
c.logger.Error("AI translate request failed", map[string]interface{}{
|
||||
@@ -216,7 +216,7 @@ func (c *Client) doPost(ctx context.Context, url string, jsonBody []byte) (*Summ
|
||||
}
|
||||
|
||||
// doTranslatePost performs a single translation POST request and parses the response.
|
||||
func (c *Client) doTranslatePost(ctx context.Context, url string, jsonBody []byte) (*TranslateResponse, error) {
|
||||
func (c *Client) doTranslatePost(ctx context.Context, url string, jsonBody []byte, requestSource string) (*TranslateResponse, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(jsonBody))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ai_summarizer: failed to create translation request: %w", err)
|
||||
@@ -253,6 +253,17 @@ func (c *Client) doTranslatePost(ctx context.Context, url string, jsonBody []byt
|
||||
"language": result.Language,
|
||||
})
|
||||
|
||||
if c.config.OnSuccessfulTranslation != nil {
|
||||
if err := c.config.OnSuccessfulTranslation(ctx, requestSource); err != nil {
|
||||
c.logger.Warn("Failed to increment AI translation success counter", map[string]interface{}{
|
||||
"notice_publication_id": result.NoticePublicationID,
|
||||
"language": result.Language,
|
||||
"request_source": requestSource,
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ai_summarizer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
@@ -15,6 +16,10 @@ type Config struct {
|
||||
APIRetryCount int // Number of retry attempts for failed API requests
|
||||
APIRetryDelay time.Duration // Delay between retry attempts
|
||||
|
||||
// OnSuccessfulTranslation is called after a successful POST /ai/translate request.
|
||||
// source identifies the caller (daily_job or manual_trigger).
|
||||
OnSuccessfulTranslation func(ctx context.Context, source string) error
|
||||
|
||||
// MinIO storage settings
|
||||
MinioEndpoint string // MinIO endpoint, e.g. "ai-host:9000"
|
||||
MinioAccessKey string // MinIO access key ID
|
||||
|
||||
@@ -109,8 +109,14 @@ type TranslateRequest struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Language string `json:"language"`
|
||||
RequestSource string `json:"-"` // internal: daily_job | manual_trigger
|
||||
}
|
||||
|
||||
const (
|
||||
TranslationRequestSourceDailyJob = "daily_job"
|
||||
TranslationRequestSourceManualTrigger = "manual_trigger"
|
||||
)
|
||||
|
||||
// TranslateResponse represents the translation payload from POST /ai/translate.
|
||||
type TranslateResponse struct {
|
||||
ContractFolderID string `json:"contract_folder_id"`
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package mongo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
mongodriver "go.mongodb.org/mongo-driver/v2/mongo"
|
||||
"go.mongodb.org/mongo-driver/v2/mongo/options"
|
||||
)
|
||||
|
||||
const (
|
||||
metricsCountersCollection = "metrics_counters"
|
||||
AITranslationSuccessCounterKey = "ai_translation_successful_requests"
|
||||
AITranslationSuccessCounterKeyDailyJob = "ai_translation_successful_requests_daily_job"
|
||||
AITranslationSuccessCounterKeyManualTrigger = "ai_translation_successful_requests_manual_trigger"
|
||||
)
|
||||
|
||||
// Counter provides atomic increment/read operations for named metrics stored in MongoDB.
|
||||
type Counter struct {
|
||||
mongo *ConnectionManager
|
||||
}
|
||||
|
||||
// NewCounter creates a counter backed by the metrics_counters collection.
|
||||
func NewCounter(mongo *ConnectionManager) *Counter {
|
||||
return &Counter{mongo: mongo}
|
||||
}
|
||||
|
||||
// Increment atomically increments the counter identified by key and returns the new value.
|
||||
func (c *Counter) Increment(ctx context.Context, key string) (int64, error) {
|
||||
if key == "" {
|
||||
return 0, fmt.Errorf("mongo counter: key is required")
|
||||
}
|
||||
|
||||
var res struct {
|
||||
Count int64 `bson:"count"`
|
||||
}
|
||||
err := c.mongo.GetCollection(metricsCountersCollection).FindOneAndUpdate(
|
||||
ctx,
|
||||
bson.M{"_id": key},
|
||||
bson.M{
|
||||
"$inc": bson.M{"count": 1},
|
||||
"$set": bson.M{"updated_at": time.Now().Unix()},
|
||||
"$setOnInsert": bson.M{"_id": key},
|
||||
},
|
||||
options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After),
|
||||
).Decode(&res)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("mongo counter increment %q: %w", key, err)
|
||||
}
|
||||
|
||||
return res.Count, nil
|
||||
}
|
||||
|
||||
// Get returns the current value of the counter identified by key (0 when missing).
|
||||
func (c *Counter) Get(ctx context.Context, key string) (int64, error) {
|
||||
if key == "" {
|
||||
return 0, fmt.Errorf("mongo counter: key is required")
|
||||
}
|
||||
|
||||
var doc struct {
|
||||
Count int64 `bson:"count"`
|
||||
}
|
||||
err := c.mongo.GetCollection(metricsCountersCollection).FindOne(ctx, bson.M{"_id": key}).Decode(&doc)
|
||||
if err != nil {
|
||||
if errors.Is(err, mongodriver.ErrNoDocuments) {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, fmt.Errorf("mongo counter get %q: %w", key, err)
|
||||
}
|
||||
|
||||
return doc.Count, nil
|
||||
}
|
||||
|
||||
// AITranslationSuccessCallback returns a callback that increments successful AI translation counters.
|
||||
func AITranslationSuccessCallback(counter *Counter) func(ctx context.Context, source string) error {
|
||||
return func(ctx context.Context, source string) error {
|
||||
if _, err := counter.Increment(ctx, AITranslationSuccessCounterKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := counter.Increment(ctx, AITranslationSuccessDailyCounterKey(time.Now().UTC())); err != nil {
|
||||
return err
|
||||
}
|
||||
if key := AITranslationSuccessCounterKeyForSource(source); key != "" {
|
||||
_, err := counter.Increment(ctx, key)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AITranslationSuccessDailyCounterKey returns the metrics key for successful translations on a given UTC day.
|
||||
func AITranslationSuccessDailyCounterKey(day time.Time) string {
|
||||
return AITranslationSuccessCounterKey + "_day:" + day.UTC().Format("2006-01-02")
|
||||
}
|
||||
|
||||
// GetDailyCounts returns counter values for each UTC day from start through end inclusive.
|
||||
func (c *Counter) GetDailyCounts(ctx context.Context, startDay, endDay time.Time) (map[string]int64, error) {
|
||||
startDay = time.Date(startDay.Year(), startDay.Month(), startDay.Day(), 0, 0, 0, 0, time.UTC)
|
||||
endDay = time.Date(endDay.Year(), endDay.Month(), endDay.Day(), 0, 0, 0, 0, time.UTC)
|
||||
if endDay.Before(startDay) {
|
||||
return map[string]int64{}, nil
|
||||
}
|
||||
|
||||
counts := make(map[string]int64)
|
||||
for day := startDay; !day.After(endDay); day = day.AddDate(0, 0, 1) {
|
||||
date := day.Format("2006-01-02")
|
||||
value, err := c.Get(ctx, AITranslationSuccessDailyCounterKey(day))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
counts[date] = value
|
||||
}
|
||||
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
// AITranslationSuccessCounterKeyForSource maps a request source to its metrics counter key.
|
||||
func AITranslationSuccessCounterKeyForSource(source string) string {
|
||||
switch source {
|
||||
case "daily_job":
|
||||
return AITranslationSuccessCounterKeyDailyJob
|
||||
case "manual_trigger":
|
||||
return AITranslationSuccessCounterKeyManualTrigger
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user