Files
chatballs/apps/backend/hub_platform/ai/limits.py
T
Andrey 3b88c980d7 fix(ai): дневной лимит стоимости в долларах + обработка LimitExceeded в ingest
Лимиты агента раньше хранились вразнобой (dailyCostMicros/dailyBudgetRub/
dailyDialogs/maxMessagesPerDialog), из них бэкенд читал только dailyCostMicros.
Из-за путаницы валют на агенте FoxRay стоял лимит dailyCostMicros:100 (= \$0.0001),
который блокировал ответы ИИ — 2 сообщения зависли без ответа.

- limits.py: ключ dailyCostUsd (целые центы USD), сравнение с расходом в micros.
  У агента FoxRay ключа нет → лимит больше не блокирует ответы.
- services.py: _normalize_limits отбрасывает устаревшие ключи при сохранении.
- ingest.py: LimitExceeded теперь ловится как ProviderError — диалог передаётся
  оператору с fallback вместо зависания (раньше исключение пробрасывалось до poller).
- AgentEditForm: одно поле «Бюджет в день, \$» вместо четырёх разнородных.
- release/model.ts: удалён мёртвый limitLabel; расходы уже везде в долларах.
- tests: обновлены под dailyCostUsd, добавлен тест на LimitExceeded в ingest.
2026-07-10 14:29:20 +03:00

33 lines
1.3 KiB
Python

from django.conf import settings
from django.db.models import Sum
from django.utils import timezone
from hub_platform.ai.models import LlmInvocation, LlmInvocationStatus
class LimitExceeded(Exception):
pass
def _day_start():
now = timezone.localtime()
return now.replace(hour=0, minute=0, second=0, microsecond=0)
def daily_cost_micros(channel=None) -> int:
queryset = LlmInvocation.objects.filter(created_at__gte=_day_start(), status=LlmInvocationStatus.SUCCESS)
if channel is not None:
queryset = queryset.filter(channel=channel)
return queryset.aggregate(total=Sum("cost_micros"))["total"] or 0
def assert_within_limits(channel, agent) -> None:
global_limit = settings.HUB_AI_GLOBAL_DAILY_COST_LIMIT_MICROS
if global_limit and daily_cost_micros() >= global_limit:
raise LimitExceeded("Global daily AI cost limit reached")
# Канальный лимит хранится в целых центах USD (dailyCostUsd); расход учитывается
# в micro-USD. 1 цент = 10 000 micro-USD.
channel_limit = (agent.limits or {}).get("dailyCostUsd")
if channel_limit and daily_cost_micros(channel) >= int(channel_limit) * 10_000:
raise LimitExceeded("Channel daily AI cost limit reached")