Merge pull request 'Refactor AI summarizer client initialization to include MongoDB manager and add statistics endpoint' (#36) from TM-602 into develop
Reviewed-on: https://repo.ravanertebat.com/TM/tm_back/pulls/36
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