diff --git a/ai.go b/ai.go index ad5f348e..02d46d8c 100644 --- a/ai.go +++ b/ai.go @@ -7174,7 +7174,18 @@ func sendAITokenLimitAlert(ctx context.Context, execution WorkflowExecution, ful cacheKey := generateAlertCacheKey(billingOrgId, "agent_token_limit_exceeded", admins) if !checkAndSetAlertCache(ctx, cacheKey) { - log.Printf("[DEBUG] Skipping duplicate AI token limit alert for org %s - already sent recently", billingOrgId) + log.Printf("[DEBUG] Skipping duplicate AI token limit alert for org %s - already sent recently (1)", billingOrgId) + return + } + + orgStats, err := GetOrgStatistics(ctx, billingOrgId) + if err != nil { + log.Printf("[ERROR] Failed to get org stats for AI token limit alert for org %s: %s", billingOrgId, err) + return + } + + if orgStats.MonthlyAIUsageAlertSent { + log.Printf("[DEBUG] Skipping duplicate AI token limit alert for org %s - already sent recently (2)", billingOrgId) return } @@ -7196,6 +7207,11 @@ func sendAITokenLimitAlert(ctx context.Context, execution WorkflowExecution, ful log.Printf("[ERROR] Failed sending AI token limit alert email to %v for org %s: %s", admins, billingOrgId, errMail) } else { log.Printf("[INFO] Sent AI token limit alert email to %v of org %s", admins, billingOrgId) + orgStats.MonthlyAIUsageAlertSent = true + errStats := SetOrgStatistics(ctx, *orgStats, billingOrgId) + if errStats != nil { + log.Printf("[ERROR] Failed to update org stats after sending AI token limit alert for org %s: %s", billingOrgId, errStats) + } } } diff --git a/db-connector.go b/db-connector.go index 2d510453..933ab62e 100755 --- a/db-connector.go +++ b/db-connector.go @@ -160,10 +160,11 @@ func SetOrgStatistics(ctx context.Context, stats ExecutionInfo, id string) error log.Printf("[ERROR] Failed adding stats with ID %s: %s", id, putErr) if strings.Contains(fmt.Sprintf("%s", putErr), "entity is too big") { - log.Printf("[WARNING] SetOrgStatistics: entity too big for org %s – archiving to GCS and trimming", id) + log.Printf("[WARNING] SetOrgStatistics: entity too big for org %s – attempting to archive to GCS", id) if archiveErr := archiveOldStatsToGCSBucket(ctx, id, &stats); archiveErr != nil { - log.Printf("[WARNING] SetOrgStatistics: GCS archive failed for org %s: %s – trimming anyway", id, archiveErr) + log.Printf("[ERROR] SetOrgStatistics: GCS archive failed for org %s: %s – cannot trim stats without backup, returning original error", id, archiveErr) + return putErr } if len(stats.DailyStatistics) > 60 { diff --git a/stats.go b/stats.go index e90a7ad3..2dad38ee 100755 --- a/stats.go +++ b/stats.go @@ -846,7 +846,7 @@ func HandleGetStatistics(resp http.ResponseWriter, request *http.Request) { // Get a max of the last 365 days if len(info.DailyStatistics) > 365 { - info.DailyStatistics = info.DailyStatistics[len(info.DailyStatistics)-60:] + info.DailyStatistics = info.DailyStatistics[len(info.DailyStatistics)-365:] } } @@ -1406,6 +1406,7 @@ func handleDailyCacheUpdate(executionInfo *ExecutionInfo) *ExecutionInfo { executionInfo.MonthlyAgentOutputTokens = 0 executionInfo.LastMonthlyResetMonth = currentMonth executionInfo.LastUsageAlertThreshold = 0 + executionInfo.MonthlyAIUsageAlertSent = false // Reset all usage alerts to unsent for index := range executionInfo.UsageAlerts { diff --git a/structs.go b/structs.go index e4f2301d..4f301bd4 100755 --- a/structs.go +++ b/structs.go @@ -513,6 +513,7 @@ type ExecutionInfo struct { LastMonthlyResetMonth int `json:"last_monthly_reset_month" datastore:"last_monthly_reset_month"` LastUsageAlertThreshold int64 `json:"last_usage_alert_threshold" datastore:"last_usage_alert_threshold"` UsageAlerts []AlertThreshold `json:"usage_alerts" datastore:"usage_alerts"` + MonthlyAIUsageAlertSent bool `json:"monthly_ai_usage_alert_sent" datastore:"monthly_ai_usage_alert_sent"` } type AdditionalUseConfig struct {