@@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-20 11:08+0300\n" +"POT-Creation-Date: 2026-08-10 11:02+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -20,7 +20,7 @@ msgstr "" "n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || " "(n%100>=11 && n%100<=14)? 2 : 3);\n" -#: authentication/exceptions/business_account.py:6 authentication/views.py:436 +#: authentication/exceptions/business_account.py:6 authentication/views.py:443 msgid "Business account not found" msgstr "Сотрудник не найден" @@ -142,7 +142,8 @@ msgstr "Домен не найден" #: authentication/exceptions/user.py:41 msgid "Failed to send the email. Verify that the email exists and is available" -msgstr "Не удалось отправить письмо. Убедитесь, что email существует и доступен" +msgstr "" +"Не удалось отправить письмо. Убедитесь, что email существует и доступен" #: authentication/models/business_account.py:18 payments/models/promocode.py:72 #: tools/public_api/models.py:34 tools/public_api/services/api_key.py:24 @@ -209,9 +210,9 @@ msgstr "Бизнес Группы" #: authentication/models/business_host.py:22 #: authentication/models/email_token.py:13 authentication/models/user.py:233 #: authentication/models/user.py:234 authentication/models/user_telegram.py:22 -#: authentication/models/user_vk.py:12 payments/admin.py:37 -#: payments/admin.py:124 payments/models/invoice.py:15 -#: payments/models/payment.py:26 payments/models/payment_plan.py:43 +#: authentication/models/user_vk.py:12 payments/admin.py:41 +#: payments/admin.py:178 payments/models/invoice.py:15 +#: payments/models/payment.py:26 payments/models/payment_plan.py:42 #: tools/media/models.py:108 msgid "User" msgstr "Пользователь" @@ -387,7 +388,7 @@ msgstr "Имя" msgid "Last name" msgstr "Фамилия" -#: authentication/models/user.py:133 +#: authentication/models/user.py:133 payments/admin.py:144 msgid "Email" msgstr "Email" @@ -515,31 +516,27 @@ msgstr "Неверный или истёкший refresh токен" msgid "User is already confirmed" msgstr "Аккаунт уже подтвержден" -#: authentication/routes/v2.py:36 authentication/views.py:489 +#: authentication/routes/v2.py:36 authentication/views.py:499 msgid "Could not confirm email, please try again." msgstr "Невозможно подтвердить email, попробуйте позже" -#: authentication/security.py:34 -#, fuzzy -#| msgid "Hidden" +#: authentication/security.py:36 msgid "Forbidden" -msgstr "Скрытый" +msgstr "Запрещено" -#: authentication/security.py:47 +#: authentication/security.py:49 msgid "Access token is expired" msgstr "Срок действия токена доступа истек" -#: authentication/security.py:49 -#, fuzzy -#| msgid "Access token is expired" +#: authentication/security.py:51 msgid "Access token invalid" -msgstr "Срок действия токена доступа истек" +msgstr "Токен доступа недействителен" -#: authentication/security.py:62 +#: authentication/security.py:79 msgid "User not found" msgstr "Пользователь не найден" -#: authentication/security.py:97 +#: authentication/security.py:114 msgid "Access token expired or does not exist" msgstr "Токен доступа просрочен или не существует" @@ -553,7 +550,7 @@ msgstr "" msgid "Host user is not registered for this account" msgstr "Пользователь бизнес-аккаунта не зарегистрирован для этого аккаунта" -#: authentication/selectors/user_selector.py:75 +#: authentication/selectors/user_selector.py:77 msgid "No user with this uid found" msgstr "Не найден пользователь с данным ID" @@ -565,52 +562,52 @@ msgstr "Бизнес-аккаунт для данного юзера не най msgid "Invited account can either accept or reject an invitation" msgstr "Приглашенный аккаунт может принять или отклонить приглашение" -#: authentication/services/email_service.py:52 +#: authentication/services/email_service.py:57 msgid "Error occured when proceed email sending" msgstr "Случилась ошибка во время отправки email" -#: authentication/services/user_services.py:167 +#: authentication/services/user_services.py:169 msgid "No user like this in a database" msgstr "Такой пользователь отсутствует" -#: authentication/services/user_services.py:184 +#: authentication/services/user_services.py:186 msgid "token is not provided" msgstr "" -#: authentication/services/user_services.py:208 +#: authentication/services/user_services.py:210 msgid "No email token provided" msgstr "Токен не получен" -#: authentication/services/user_services.py:218 +#: authentication/services/user_services.py:220 msgid "Passwords do not match" msgstr "Пароли не совпадают" -#: authentication/services/user_services.py:256 +#: authentication/services/user_services.py:258 msgid "Current password is wrong" msgstr "Текущий пароль неверен" -#: authentication/views.py:133 authentication/views.py:242 -#: authentication/views.py:348 authentication/views.py:381 +#: authentication/views.py:134 authentication/views.py:245 +#: authentication/views.py:353 authentication/views.py:387 msgid "Server error occured" msgstr "Случилась серверная ошибка" -#: authentication/views.py:238 +#: authentication/views.py:239 msgid "Email not found" msgstr "Email не найден" -#: authentication/views.py:318 authentication/views.py:370 +#: authentication/views.py:322 authentication/views.py:375 msgid "Email sending error: email not found" msgstr "Ошибка отправки письма: email не найден" -#: authentication/views.py:340 +#: authentication/views.py:345 msgid "Business account has been deleted" msgstr "Сотрудник успешно удален" -#: authentication/views.py:368 +#: authentication/views.py:372 msgid "Business account has been reinvited" msgstr "Повторное приглашение сотруднику успешно отправлено" -#: authentication/views.py:440 +#: authentication/views.py:449 msgid "Business account password has been updated" msgstr "Пароль сотрудника успешно обновлен" @@ -637,12 +634,10 @@ msgid "A %(model)s with fields %(fields)s already exists" msgstr "Уже существует %(model)s с полями %(fields)s" #: lib/parsers.py:18 -#, fuzzy -#| msgid "Invalid info payload" msgid "Invalid JSON payload" -msgstr "Некорректные данные в поле info" +msgstr "Некорректный JSON" -#: messages/serializers.py:44 ml_model/exceptions.py:86 +#: messages/serializers.py:44 ml_model/exceptions.py:89 #: tools/chats/schemas.py:23 #, python-format msgid "The file size cannot exceed %(max_mb_size)d MB" @@ -657,16 +652,16 @@ msgstr "Версия %(version)s уже имеет входные данные msgid "Neuron Models" msgstr "Нейронные Модели" -#: ml_model/exceptions.py:20 +#: ml_model/exceptions.py:21 msgid "The model is currently disabled. Please try again later." msgstr "" "Модель в настоящее время неактивна. Пожалуйста, повторите попытку позже." -#: ml_model/exceptions.py:25 +#: ml_model/exceptions.py:26 msgid "Your request was blocked by our moderation system" msgstr "Ваш запрос был заблокирован нашей системой модерации" -#: ml_model/exceptions.py:35 +#: ml_model/exceptions.py:36 #, python-format msgid "" "Image size %(cw)dx%(ch)d is not supported. Please rotate image to " @@ -675,25 +670,25 @@ msgstr "" "Размер изображения %(cw)dx%(ch)d не поддерживается. Пожалуйста, переверните " "до %(rw)dx%(rh)d" -#: ml_model/exceptions.py:38 +#: ml_model/exceptions.py:39 #, python-format msgid "Image size %(cw)sx%(ch)s is not supported. Required size: %(rw)sx%(rh)s" msgstr "" "Размер изображения %(cw)sx%(ch)s не поддерживается. Требуемый размер: " "%(rw)sx%(rh)s" -#: ml_model/exceptions.py:47 +#: ml_model/exceptions.py:49 #, python-format msgid "Image exceeds the maximum allowed pixel count (%(max_pixels)d)." msgstr "" "Размер изображения превышает максимально допустимое количество пикселей " "(%(max_pixels)d)." -#: ml_model/exceptions.py:53 +#: ml_model/exceptions.py:56 msgid "The model is not responding" msgstr "Модель не отвечает" -#: ml_model/exceptions.py:62 +#: ml_model/exceptions.py:65 #, python-format msgid "" "The attached file format is not supported. Available formats: " @@ -702,97 +697,108 @@ msgstr "" "Формат вложенного файла не поддерживается. Доступные форматы: " "%(available_extensions)s." -#: ml_model/exceptions.py:68 +#: ml_model/exceptions.py:71 msgid "The file may be corrupted. Please try another one." msgstr "Возможно, файл повреждён. Попробуйте загрузить другой файл." -#: ml_model/exceptions.py:73 +#: ml_model/exceptions.py:76 msgid "File Uploading Not supported" msgstr "Загрузка файлов не поддерживается" -#: ml_model/exceptions.py:78 +#: ml_model/exceptions.py:81 msgid "Unable to recognize the file" msgstr "Не удаётся распознать файл" -#: ml_model/exceptions.py:91 +#: ml_model/exceptions.py:94 msgid "The length of the context has been exceeded." msgstr "Длина контекста превышена." -#: ml_model/exceptions.py:96 +#: ml_model/exceptions.py:99 msgid "Jinja template not found" msgstr "Jinja-шаблон не найден" -#: ml_model/exceptions.py:101 +#: ml_model/exceptions.py:104 msgid "There was an unknown error while rendering a template" msgstr "При рендеринге шаблона произошла неизвестная ошибка" -#: ml_model/exceptions.py:106 +#: ml_model/exceptions.py:109 msgid "The neuron model does not exist" msgstr "Нейронная модель не существует" -#: ml_model/exceptions.py:114 +#: ml_model/exceptions.py:117 #, python-format msgid "The %(file_type)s is not attached" msgstr "Файл (%(file_type)s) не прикреплен" -#: ml_model/exceptions.py:119 +#: ml_model/exceptions.py:122 msgid "No image content found in response. Try a different request" msgstr "В промпте отсутствует описание изображения. Попробуйте другой запрос" -#: ml_model/exceptions.py:124 +#: ml_model/exceptions.py:127 msgid "" "The model could not analyze your request. Please rephrase it and try again" msgstr "" "Модель не смогла проанализировать ваш запрос. Перефразируйте его и " "попробуйте снова" -#: ml_model/exceptions.py:129 +#: ml_model/exceptions.py:132 msgid "Image analysis error. Please try another image." msgstr "Ошибка анализа изображения. Попробуйте другую картинку." -#: ml_model/exceptions.py:134 +#: ml_model/exceptions.py:137 msgid "Use style type AUTO or GENERAL when a style preset is selected" msgstr "При выбранном стиле используйте тип стиля AUTO или GENERAL" -#: ml_model/exceptions.py:139 +#: ml_model/exceptions.py:142 msgid "Prediction interrupted. Please retry again" msgstr "Генерация прервана. Пожалуйста, повторите попытку еще раз" -#: ml_model/exceptions.py:154 +#: ml_model/exceptions.py:159 #, python-format msgid "Prompt is too long. Maximum length is %(max_length)s characters." msgstr "Промпт слишком длинный. Максимальная длина — %(max_length)s символов." -#: ml_model/exceptions.py:161 +#: ml_model/exceptions.py:162 +msgid "Prompt is too long" +msgstr "Промпт слишком длинный" + +#: ml_model/exceptions.py:167 +msgid "" +"Service is currently unavailable due to high demand. Please try again later" +msgstr "" +"Сервис временно недоступен из-за высокой нагрузки. Пожалуйста, попробуйте позже" + +#: ml_model/exceptions.py:172 msgid "Service is temporarily unavailable. Please try again later" msgstr "Сервис временно недоступен. Пожалуйста, попробуйте позже" -#: ml_model/exceptions.py:169 +#: ml_model/exceptions.py:192 #, python-format msgid "%(feature)s is available only in paid plan." msgstr "%(feature)s доступно только в платном тарифном плане." -#: ml_model/exceptions.py:176 +#: ml_model/exceptions.py:199 msgid "Face not found in the image. Please try another image with a face." msgstr "Не найдено лицо на картинке. Попробуйте другую картинку с лицом." -#: ml_model/exceptions.py:181 +#: ml_model/exceptions.py:204 msgid "The input image may contain real person." msgstr "Загруженное изображение может содержать реального человека." -#: ml_model/exceptions.py:186 +#: ml_model/exceptions.py:209 msgid "The generated image may contain private or prohibited content" msgstr "Готовое изображение может содержать приватный или запрещённый контент" -#: ml_model/exceptions.py:208 +#: ml_model/exceptions.py:214 msgid "The input image may contain private or prohibited content" -msgstr "Загруженное изображение может содержать приватный или запрещённый контент" +msgstr "" +"Загруженное изображение может содержать приватный или запрещённый контент" -#: ml_model/exceptions.py:215 +#: ml_model/exceptions.py:223 msgid "not specified" msgstr "не указана" -#: ml_model/exceptions.py:197 +#: ml_model/exceptions.py:224 #, python-format msgid "" "Version \"%(version)s\" is not available. Available versions: " @@ -858,7 +864,7 @@ msgstr "Теги" msgid "Neuron Model" msgstr "Нейронная Модель" -#: ml_model/models.py:166 ml_model/models.py:413 payments/admin.py:130 +#: ml_model/models.py:166 ml_model/models.py:413 payments/admin.py:184 msgid "Model" msgstr "Модель" @@ -1087,26 +1093,25 @@ msgstr "Инструкция Модели" msgid "Model Instructions" msgstr "Инструкции Моделей" -#: ml_model/selectors/ml_models_selector.py:114 +#: ml_model/selectors/ml_models_selector.py:116 msgid "no model by this id" msgstr "Не найдено моделей по этому ID" -#: ml_model/services/FileService.py:110 tools/media/apis.py:280 -#: tools/public_api/views/ml_service.py:56 +#: ml_model/services/FileService.py:110 #: tools/public_api/views/providers/openai_compatible.py:208 msgid "Voice not found." msgstr "Голос не найден." -#: ml_model/services/chatgpt.py:244 +#: ml_model/services/chatgpt.py:245 msgid "Image is ready" msgstr "Изображение готово" -#: ml_model/services/chatgpt.py:360 ml_model/services/claude.py:266 -#: ml_model/services/grok.py:190 +#: ml_model/services/chatgpt.py:361 ml_model/services/claude.py:277 +#: ml_model/services/grok.py:191 msgid "File analysis" msgstr "Анализ файлов" -#: ml_model/services/chatgpt.py:382 ml_model/services/chatgpt_5.py:133 +#: ml_model/services/chatgpt.py:383 ml_model/services/chatgpt_5.py:133 msgid "The \"Use code\" option cannot be used together with an attached image." msgstr "" "Нельзя одновременно использовать параметр «Использовать код» вместе с " @@ -1133,24 +1138,36 @@ msgstr "3К разрешение не поддерживается для это msgid "No image given for improving" msgstr "Нет изображения для улучшения" -#: ml_model/tasks.py:144 +#: ml_model/tasks.py:184 msgid "Lyrics is too long" msgstr "Текст песни слишком длинный" +#: ml_model/validators.py:48 tools/public_api/routes/providers/openai.py:58 +msgid "The request must not be empty" +msgstr "Запрос не должен быть пустым" + #: ml_model/views.py:65 msgid "Model data cannot be retrieved" msgstr "Невозможно получить данные модели" -#: payments/admin.py:35 payments/admin.py:76 payments/admin.py:122 +#: payments/admin.py:39 payments/admin.py:98 payments/admin.py:175 msgid "You can search by user email, exacted company name" msgstr "" "Вы можете осуществлять поиск по e-mail пользователя, точному названию " "компании" -#: payments/admin.py:40 payments/admin.py:127 +#: payments/admin.py:44 payments/admin.py:181 msgid "Missing" msgstr "Отсутствующий" +#: payments/admin.py:157 payments/models/user_payment_method.py:23 +msgid "Gateway" +msgstr "Шлюз" + +#: payments/admin.py:161 payments/models/user_payment_method.py:24 +msgid "Payment method UID" +msgstr "UID платёжного метода" + #: payments/apps.py:11 payments/models/payment.py:60 msgid "Payments" msgstr "Платежи" @@ -1172,6 +1189,34 @@ msgstr "" msgid "The payer does not exist" msgstr "Плательщик не существует" +#: payments/exceptions/subscription_recovery.py:6 +msgid "Active payment method not found" +msgstr "Активный способ оплаты не найден" + +#: payments/exceptions/subscription_recovery.py:11 +msgid "Subscription recovery is already in progress" +msgstr "Восстановление подписки уже выполняется" + +#: payments/models/attempt.py:12 payments/models/user_payment_method.py:48 +msgid "Payment Method" +msgstr "Платежный метод" + +#: payments/models/attempt.py:15 +msgid "Cancel Reason" +msgstr "Причина отмены" + +#: payments/models/attempt.py:16 +msgid "In Cycle" +msgstr "" + +#: payments/models/attempt.py:22 +msgid "Payment Attempt" +msgstr "Попытка платежа" + +#: payments/models/attempt.py:23 +msgid "Payment Attempts" +msgstr "Попытки платежа" + #: payments/models/invoice.py:23 msgid "Generative Model" msgstr "Генеративная модель" @@ -1200,56 +1245,60 @@ msgstr "Статус" msgid "Payment" msgstr "Платеж" -#: payments/models/payment_plan.py:13 +#: payments/models/payment_plan.py:12 msgid "Price" msgstr "Цена" -#: payments/models/payment_plan.py:17 +#: payments/models/payment_plan.py:16 msgid "Tokens per plan" msgstr "Токенов за план" -#: payments/models/payment_plan.py:20 +#: payments/models/payment_plan.py:19 msgid "Is corporate" msgstr "Корпоративный" -#: payments/models/payment_plan.py:21 +#: payments/models/payment_plan.py:20 msgid "Individual" msgstr "Индивидуальный" -#: payments/models/payment_plan.py:22 +#: payments/models/payment_plan.py:21 msgid "Is visible" msgstr "Видимый" -#: payments/models/payment_plan.py:34 payments/models/payment_plan.py:49 +#: payments/models/payment_plan.py:33 payments/models/payment_plan.py:48 #: payments/models/payment_plan_feature.py:16 msgid "Payment Plan" msgstr "Платежный План" -#: payments/models/payment_plan.py:35 +#: payments/models/payment_plan.py:34 msgid "Payment Plans" msgstr "Платежные Планы" -#: payments/models/payment_plan.py:51 +#: payments/models/payment_plan.py:50 msgid "Last payment at" msgstr "Последнее время платежа" -#: payments/models/payment_plan.py:52 +#: payments/models/payment_plan.py:51 msgid "Next payment at" msgstr "Следующее время платежа" -#: payments/models/payment_plan.py:56 payments/models/user_payment_method.py:25 -msgid "Payment Method" -msgstr "Платежный метод" +#: payments/models/payment_plan.py:56 +msgid "Last recovery payment id" +msgstr "ID последнего платежа восстановления" #: payments/models/payment_plan.py:62 +msgid "Recovery locked at" +msgstr "Время блокировки восстановления" + +#: payments/models/payment_plan.py:65 msgid "Current balance" msgstr "Текущий баланс" -#: payments/models/payment_plan.py:68 +#: payments/models/payment_plan.py:71 msgid "Referral balance" msgstr "Реферальный баланс" -#: payments/models/payment_plan.py:89 payments/models/payment_plan.py:90 +#: payments/models/payment_plan.py:98 payments/models/payment_plan.py:99 msgid "User Balance" msgstr "Баланс пользователя" @@ -1321,75 +1370,67 @@ msgstr "Активация Промокода" msgid "Promocode Activations" msgstr "Активации Промокодов" -#: payments/models/user_payment_method.py:9 +#: payments/models/user_payment_method.py:10 msgid "Bank Card" msgstr "Банковская карта" -#: payments/models/user_payment_method.py:10 +#: payments/models/user_payment_method.py:11 msgid "Mir Pay" msgstr "Mir Pay" -#: payments/models/user_payment_method.py:11 +#: payments/models/user_payment_method.py:12 msgid "Sberbank" msgstr "Сбербанк" -#: payments/models/user_payment_method.py:12 +#: payments/models/user_payment_method.py:13 msgid "YooMoney" msgstr "ЮMoney" -#: payments/models/user_payment_method.py:13 +#: payments/models/user_payment_method.py:14 msgid "T-bank" msgstr "Т-банк" -#: payments/models/user_payment_method.py:14 +#: payments/models/user_payment_method.py:15 msgid "SBP" msgstr "СБП" -#: payments/models/user_payment_method.py:16 -msgid "Gateway" -msgstr "Шлюз" +#: payments/models/user_payment_method.py:20 +msgid "User Plan Info" +msgstr "Информация о плане пользователя" -#: payments/models/user_payment_method.py:17 -msgid "Payment method UID" -msgstr "UID платёжного метода" - -#: payments/models/user_payment_method.py:18 tools/media/models.py:78 +#: payments/models/user_payment_method.py:25 tools/media/models.py:78 msgid "Meta" msgstr "Метаданные" -#: payments/models/user_payment_method.py:19 -msgid "Attempts" -msgstr "Попытки" - #: payments/models/user_payment_method.py:26 +msgid "Active" +msgstr "Активен" + +#: payments/models/user_payment_method.py:27 +msgid "Primary" +msgstr "" + +#: payments/models/user_payment_method.py:49 msgid "Payment Methods" msgstr "Платежные методы" -#: payments/routes/v1.py:93 +#: payments/routes/v1.py:110 msgid "You do not have an active subscription to cancel" msgstr "У вас нет активной подписки для отмены" -#: payments/routes/v1.py:94 +#: payments/routes/v1.py:111 msgid "The recurring payment is successfully cancelled" msgstr "Автоплатежи успешно отключены" -#: payments/exceptions/subscription_recovery.py -msgid "Active payment method not found" -msgstr "Активный способ оплаты не найден" - -#: payments/exceptions/subscription_recovery.py -msgid "Subscription recovery is already in progress" -msgstr "Восстановление подписки уже выполняется" - -#: payments/routes/v1.py +#: payments/routes/v1.py:129 msgid "Payment could not be completed, please try again later" msgstr "Не удалось провести платёж, пожалуйста, попробуйте позже" -#: payments/routes/v1.py:144 +#: payments/routes/v1.py:199 msgid "Expenses" msgstr "Затраты" -#: payments/routes/v1.py:148 +#: payments/routes/v1.py:203 msgid "Refills" msgstr "Пополнения" @@ -1439,8 +1480,8 @@ msgstr "Публичный API" msgid "Media" msgstr "Медиа" -#: tools/chats/apis.py:201 tools/media/apis.py:229 -#: tools/public_api/views/base.py:102 +#: tools/chats/apis.py:219 tools/media/apis.py:246 +#: tools/public_api/views/base.py:108 msgid "" "An unexpected generation error has occurred. Please try again later or use a " "different model" @@ -1448,7 +1489,7 @@ msgstr "" "Произошла непредвиденная ошибка при генерации. Пожалуйста попробуйте позже " "или используйте другую модель" -#: tools/chats/apis.py:257 +#: tools/chats/apis.py:275 msgid "The message has already been deleted" msgstr "Сообщение уже было удалено" @@ -1461,25 +1502,23 @@ msgstr "Чат %(id)s" msgid "Chat" msgstr "Чат" -#: tools/chats/routes/v1.py:37 tools/public_api/routes/v1.py:66 -#, fuzzy -#| msgid "User not found" +#: tools/chats/routes/v1.py:39 tools/public_api/routes/v1.py:68 msgid "Stream not found" -msgstr "Пользователь не найден" +msgstr "Стрим не найден" -#: tools/chats/routes/v1.py:51 +#: tools/chats/routes/v1.py:53 msgid "Chat not found" msgstr "Чат не найден" -#: tools/chats/routes/v1.py:54 tools/public_api/routes/v1.py:106 +#: tools/chats/routes/v1.py:56 tools/public_api/routes/v1.py:108 msgid "Stream not supported for this model" msgstr "Стриминг не поддерживается для этой модели" -#: tools/chats/routes/v1.py:58 +#: tools/chats/routes/v1.py:60 msgid "Stream already in progress" msgstr "" -#: tools/chats/schemas.py:34 tools/public_api/views/ml_service.py:88 +#: tools/chats/schemas.py:34 tools/public_api/views/ml_service.py:60 msgid "Invalid info payload" msgstr "Некорректные данные в поле info" @@ -1487,7 +1526,7 @@ msgstr "Некорректные данные в поле info" msgid "Stream timeout" msgstr "" -#: tools/media/apis.py:221 +#: tools/media/apis.py:238 msgid "" "Temporary issues with the service, we are already working on a solution." msgstr "Временные неполадки с сервисом, мы уже работаем над их решением." @@ -1574,21 +1613,12 @@ msgid "API Keys" msgstr "API Ключи" #: tools/public_api/routes/providers/openai.py:19 -#, fuzzy -#| msgid "Missing required parameter: model_id" msgid "Missing required parameter: input" -msgstr "Отсутствует обязательный параметр: 'model_id'" +msgstr "Отсутствует обязательный параметр: 'input'" #: tools/public_api/routes/providers/openai.py:24 -#, fuzzy -#| msgid "Invalid info payload" msgid "Invalid input payload" -msgstr "Некорректные данные в поле info" - -#: tools/public_api/routes/providers/openai.py:58 -#: tools/public_api/routes/v1.py:109 tools/public_api/views/base.py:75 -msgid "The request must not be empty" -msgstr "Запрос не должен быть пустым" +msgstr "Некорректные данные в поле input" #: tools/public_api/routes/providers/openai.py:85 #: tools/public_api/views/providers/elevenlabs_compatible.py:147 @@ -1598,10 +1628,8 @@ msgid "Model not found" msgstr "Модель не найдена" #: tools/public_api/routes/providers/openai.py:118 -#, fuzzy -#| msgid "File Uploading Not supported" msgid "Only streaming is supported" -msgstr "Загрузка файлов не поддерживается" +msgstr "Поддерживается только стриминг" #: tools/public_api/routes/providers/openai.py:120 #: tools/public_api/views/providers/openai_compatible.py:61 @@ -1616,41 +1644,33 @@ msgstr "" msgid "message_uuid is not provided" msgstr "" -#: tools/public_api/routes/v1.py:31 +#: tools/public_api/routes/v1.py:33 msgid "No API Key in Authorization header" msgstr "" -#: tools/public_api/routes/v1.py:43 -#, fuzzy -#| msgid "API Key not found" +#: tools/public_api/routes/v1.py:45 msgid "API key not found" msgstr "API-ключ не найден" -#: tools/public_api/routes/v1.py:46 -#, fuzzy -#| msgid "Access token is expired" +#: tools/public_api/routes/v1.py:48 msgid "API key expired" -msgstr "Срок действия токена доступа истек" +msgstr "Срок действия API-ключа истёк" -#: tools/public_api/routes/v1.py:48 -#, fuzzy -#| msgid "Key limit exceeded" +#: tools/public_api/routes/v1.py:50 msgid "API key limit exceeded" -msgstr "Превышен лимит по ключу" +msgstr "Превышен лимит API-ключа" -#: tools/public_api/routes/v1.py:62 tools/public_api/routes/v1.py:96 -#, fuzzy -#| msgid "Host user is not registered for this account" +#: tools/public_api/routes/v1.py:64 tools/public_api/routes/v1.py:98 msgid "API key is not available for this account type" -msgstr "Пользователь бизнес-аккаунта не зарегистрирован для этого аккаунта" +msgstr "API-ключ недоступен для этого типа аккаунта" -#: tools/public_api/routes/v1.py:104 tools/public_api/views/base.py:68 +#: tools/public_api/routes/v1.py:106 tools/public_api/views/base.py:69 msgid "Model is blocked by outdating or temporary block, please retry later" msgstr "" "Модель заблокирована, т.к закончила обновляться или временно заблокирована, " "попробуйте позже" -#: tools/public_api/views/base.py:63 +#: tools/public_api/views/base.py:64 msgid "Key limit exceeded" msgstr "Превышен лимит по ключу" @@ -1695,6 +1715,9 @@ msgstr "Название голоса успешно обновлено" msgid "Preset voices are shared and cannot be deleted. Use your own voice id." msgstr "Пресеты общие и не удаляются. Используйте id собственного голоса." +#~ msgid "Attempts" +#~ msgstr "Попытки" + #, python-format #~ msgid "This video duration is not allowed for %(quality)s quality." #~ msgstr "" @@ -1741,9 +1764,6 @@ msgstr "Пресеты общие и не удаляются. Используй #~ msgid "Is recurrent" #~ msgstr "Рекуррентный" -#~ msgid "Payment Datetime" -#~ msgstr "Дата и время платежа" - #~ msgid "Recurring Payment" #~ msgstr "Автоплатеж" @@ -1,4 +1,5 @@ import json +import re from enum import StrEnum import logging import time @@ -15,6 +16,7 @@ from ml_model.exceptions import ( OutputSensitiveImageContentError, RealPersonDetectedError, RequestBlocked, + PromptLengthExceeded, ) from poller.models import Proxy from tools.chats.domain import RawSSEChunk @@ -114,6 +116,26 @@ class BytedanceModelArkAdapter: return 'video_url' return 'image_url' + @classmethod + def _handle_error_response(cls, resp: httpx.Response) -> None: + if resp.status_code == 413: + raise PromptLengthExceeded + + if 'Input length' in resp.text and 'exceeds the maximum length' in resp.text: + match = re.search(r"Input length (\d+) exceeds the maximum length (\d+)", resp.text) + if match: + max_length = int(match.group(2)) + raise PromptLengthExceeded(max_length=max_length) + raise PromptLengthExceeded + + logger.error( + 'Bytedance request failed status=%s route=%s body=%s', + resp.status_code, + cls.CONTENT_TYPE_TO_ENDPOINT.get(BytedanceContentType.CHAT), + resp.text, + ) + raise GenerationException + @classmethod def _raise_by_error_payload(cls, data: dict[str, Any], choices: list[dict[str, Any]]) -> None: choice_reasons = {str(choice.get('finish_reason', '')).lower() for choice in choices} @@ -208,12 +230,7 @@ class BytedanceModelArkAdapter: ) as client: resp = client.post(cls.CONTENT_TYPE_TO_ENDPOINT[BytedanceContentType.CHAT], json=payload) if resp.status_code >= 400: - logger.error( - 'Bytedance request failed status=%s route=%s body=%s', - resp.status_code, - cls.CONTENT_TYPE_TO_ENDPOINT[BytedanceContentType.CHAT], - resp.text, - ) + cls._handle_error_response(resp) try: data: BytedanceChatResponse = resp.json() except Exception as exc: @@ -279,13 +296,7 @@ class BytedanceModelArkAdapter: 'POST', cls.CONTENT_TYPE_TO_ENDPOINT[BytedanceContentType.CHAT], json=payload ) as resp: if resp.status_code >= 400: - logger.error( - 'Bytedance request failed status=%s route=%s body=%s', - resp.status_code, - cls.CONTENT_TYPE_TO_ENDPOINT[BytedanceContentType.CHAT], - resp.text, - ) - raise GenerationException + cls._handle_error_response(resp) usage: BytedanceUsage = {} for line in resp.iter_lines(): if not line: @@ -70,14 +70,15 @@ class OpenrouterAdapter: try: data_obj = json.loads(data) - content_chunk = data_obj['choices'][0]['delta'].get('content') or '' - reasoning_chunk = data_obj['choices'][0]['delta'].get('reasoning') or '' - if content_chunk: - content += content_chunk - yield RawSSEChunk(event='token', data={'content': content_chunk}) - if reasoning_chunk: - reasoning += reasoning_chunk - yield RawSSEChunk(event='think', data={'content': reasoning_chunk}) + if data_obj.get('choices'): + content_chunk = data_obj['choices'][0].get('delta', {}).get('content') or '' + reasoning_chunk = data_obj['choices'][0].get('delta', {}).get('reasoning') or '' + if content_chunk: + content += content_chunk + yield RawSSEChunk(event='token', data={'content': content_chunk}) + if reasoning_chunk: + reasoning += reasoning_chunk + yield RawSSEChunk(event='think', data={'content': reasoning_chunk}) if data_obj.get('usage'): input_tokens = data_obj['usage']['prompt_tokens'] output_tokens = data_obj['usage']['completion_tokens'] @@ -1,21 +1,33 @@ import re import subprocess import zipfile +from functools import wraps from uuid import UUID import docx2txt +import filetype import fitz import openpyxl from io import BytesIO +from PIL import Image +from django.core.files.images import get_image_dimensions from django.db.models.fields.files import FieldFile from django.utils.translation import gettext as _ from authentication.models import CustomUserModel -from ml_model.exceptions import InvalidParameterError, UnrecognizedFileError +from ml_model.exceptions import ( + CorruptedFileError, + FileExtensionNotSupported, + ImageTooLargeError, + InvalidParameterError, + UnrecognizedFileError, +) from tools.media.models import Voice, Preset, PresetKind +# FIXME: Переработать сервисы по работе с файлами. Возможно, прибегнуть к использованию миксинов + class FileProcessingService: @classmethod @@ -110,3 +122,54 @@ class FileProcessingService: raise InvalidParameterError(_('Voice not found.')) from exc return voice.file + +class ImageFileProcessingService: + ALLOWED_EXTENSIONS = ['PNG', 'JPG', 'JPEG', 'WEBP'] + + def __init__(self, image: FieldFile) -> None: + self.image = image + + @staticmethod + def __reset_image(func): + @wraps(func) + def wrapper(self, *args, **kwargs): + try: + return func(self, *args, **kwargs) + finally: + self.image.seek(0) + + return wrapper + + @__reset_image + def get_bytes(self, size: int | None = None) -> bytes: + return self.image.read(size) + + def get_kind(self, file_bytes: bytes): + kind = filetype.guess(file_bytes) + if not kind: + raise CorruptedFileError + if kind.extension.upper() not in self.ALLOWED_EXTENSIONS: + raise FileExtensionNotSupported(self.ALLOWED_EXTENSIONS) + return kind + + @__reset_image + def get_dimensions(self, max_pixels: int) -> tuple[int, int]: + try: + w, h = get_image_dimensions(self.image) + if not (w and h): + raise CorruptedFileError + if w * h > max_pixels: + raise ImageTooLargeError(max_pixels) + return w, h + except Image.DecompressionBombError: + raise ImageTooLargeError(max_pixels) + + def get_normalized_image(self, file_bytes: bytes) -> BytesIO: + normalized_image = BytesIO(file_bytes) + with Image.open(normalized_image) as source_image: + img = source_image.convert('RGBA') + normalized_image = BytesIO() + img.save(normalized_image, format='PNG') + img.close() + normalized_image.seek(0) + return normalized_image @@ -7,12 +7,12 @@ from typing import Any import requests from django.utils.translation import gettext as _ from django.core.files import File -from replicate.exceptions import ModelError from messages.models import Message from ml_model.adapters.bytedance_model_ark import BytedanceContentType from ml_model.exceptions import InvalidParameterError from ml_model.exceptions import ModelVersionNotAvailable +from ml_model.services.FileService import ImageFileProcessingService from ml_model.services.base import SimpleService from ml_model.tasks import bytedance_model_ark_run from payments.exceptions.insufficient_balance import InsufficientBalance @@ -94,6 +94,11 @@ class Seedream(SimpleService): **input_message.info, } if image := input_message.file: + image_processor = ImageFileProcessingService(image) + file_bytes = image_processor.get_bytes(20) + image_processor.get_kind(file_bytes) + image_processor.get_dimensions(max_pixels=36000000) + image_processor.image.close() callback_data.update({'image': image.url}) images = bytedance_model_ark_run( @@ -40,6 +40,7 @@ class UnsupportedSize(Exception): self.current_size | self.required_size ) + class ImageTooLargeError(Exception): def __init__(self, max_pixels: int) -> None: self.max_pixels = max_pixels @@ -49,6 +50,7 @@ class ImageTooLargeError(Exception): 'max_pixels': self.max_pixels } + class ModelTimeoutError(Exception): def __str__(self): return _('The model is not responding') @@ -147,14 +149,17 @@ class InvalidParameterError(Exception): def __str__(self) -> str: return str(self.error_text) + class PromptLengthExceeded(Exception): - def __init__(self, max_length: int = 3000) -> None: + def __init__(self, max_length: int | None = None) -> None: self.max_length = max_length def __str__(self) -> str: - return _('Prompt is too long. Maximum length is %(max_length)s characters.') % { - 'max_length': self.max_length - } + if self.max_length: + return _('Prompt is too long. Maximum length is %(max_length)s characters.') % { + 'max_length': self.max_length + } + return _('Prompt is too long') class ServiceHighDemandError(Exception): @@ -216,9 +221,7 @@ class ModelVersionNotAvailable(Exception): def __str__(self) -> str: version_label = self.version if self.version is not None else _('not specified') - return _( - 'Version "%(version)s" is not available. Available versions: %(available_versions)s.' - ) % { + return _('Version "%(version)s" is not available. Available versions: %(available_versions)s.') % { 'version': version_label, 'available_versions': ', '.join(self.available_versions), }