@@ -298,6 +298,10 @@ CELERY_BEAT_SCHEDULE = { 'task': 'payments.tasks.send_low_balance_message', 'schedule': crontab(0, 8), }, + 'dump_messages_errors': { + 'task': 'messages.tasks.dump_messages_errors', + 'schedule': crontab(0) + } } CACHES = { @@ -1218,3 +1218,15 @@ msgstr "Токен доступа просрочен или не существ msgid "Model data cannot be retrieved" msgstr "Невозможно получить данные модели" + +msgid "Message" +msgstr "Сообщение" + +msgid "Detail" +msgstr "Подробности" + +msgid "Message error" +msgstr "Ошибка сообщения" + +msgid "Message errors" +msgstr "Ошибки сообщения" @@ -0,0 +1,29 @@ +# Generated by Django 5.0.11 on 2025-09-22 10:36 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('msgs', '0005_alter_message_content_type_alter_message_object_id_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='MessageError', + fields=[ + ('uid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создан')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Изменён')), + ('detail', models.TextField(verbose_name='Detail')), + ('message', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='message', to='msgs.message', verbose_name='Message')), + ], + options={ + 'verbose_name': 'Message error', + 'verbose_name_plural': 'Message errors', + }, + ), + ] @@ -1,2 +1,3 @@ from .message import Message, message_file_upload +from .message_error import MessageError from .store import BaseStore, SingleStore, MultipleStore @@ -0,0 +1,17 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from core.models import BaseModel +from .message import Message + + +class MessageError(BaseModel): + message = models.ForeignKey(Message, on_delete=models.DO_NOTHING, verbose_name=_('Message'), related_name='message') + detail = models.TextField(verbose_name=_('Detail')) + + class Meta: + verbose_name = _('Message error') + verbose_name_plural = _('Message errors') + + def __str__(self) -> str: + return f'{self.detail[:50]}' @@ -0,0 +1,8 @@ +from django.contrib import admin + +from messages.models import MessageError + + +@admin.register(MessageError) +class MessageErrorAdmin(admin.ModelAdmin): + list_display = ['message', 'detail'] \ No newline at end of file @@ -0,0 +1,15 @@ +import json + +from django.core.cache import cache + +from celery import shared_task + +from .models import MessageError + + +@shared_task +def dump_messages_errors(): + pipe = cache.client.get_client().pipeline() + pipe.lrange('message_errors', 0, -1) + pipe.ltrim('message_errors', 1, 0) + MessageError.objects.bulk_create([MessageError(**json.loads(data)) for data in pipe.execute()[0]]) @@ -1,6 +1,10 @@ +import json + from django.urls import path +from django.core.cache import cache from .apis import MessageAPIView, MessagesAPIView +from .models import Message def make_messages_urls(appname: str): @@ -16,3 +20,10 @@ def make_messages_urls(appname: str): name='message', ), ] + +def push_message_error(message: Message, err: str) -> None: + err_data = { + 'message_id': str(message.uid), + 'detail': err + } + cache.client.get_client().rpush('message_errors', json.dumps(err_data)) @@ -426,7 +426,6 @@ class Chatgpt(SimpleService): ) msgs = self.save_results([response], process_time, save) return msgs - raise GenerationException def get_chat_history(self, model_name: str) -> InMemoryChatMessageHistory: if isinstance(self.store, Chat): @@ -195,7 +195,7 @@ class Raifgpt(Chatgpt): except BadRequestError as exc: if exc.code == 'context_length_exceeded': raise ExceededContextLengthError - raise GenerationException + raise output_tokens = self.count_text_tokens([response]) @@ -19,6 +19,7 @@ from rest_framework.views import APIView from messages.models import Message from messages.serializers import MessageSerializer +from messages.utils import push_message_error from ml_model.exceptions import DeploymentDisabled, TemplateNotFound, TemplateUnknownException, \ FileExtensionNotSupported, ExceededContextLengthError from ml_model.services.base import SimpleService @@ -174,6 +175,7 @@ class MessagesAPIView(APIView): if isinstance(exc, InsufficientBalance): return Response({'detail': f'{exc}'}, status=HTTP_402_PAYMENT_REQUIRED) logger.exception(exc) + push_message_error(input_message, str(exc)) return Response( { 'detail': _( @@ -8,6 +8,7 @@ from rest_framework.views import APIView from messages.models import Message from messages.serializers import MessageSerializer +from messages.utils import push_message_error from ml_model.models import NeuronModel from ml_model.services.base import SimpleService @@ -146,6 +147,7 @@ class MediaAPIView(APIView): logger.exception(exc) input_message.is_sent = False input_message.save() + push_message_error(input_message, str(exc)) return Response(f'Error: {exc}', status=400) return Response(MessageSerializer(output_messages, many=True).data, 201) else: