@@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-11-14 14:23+0300\n" +"POT-Creation-Date: 2025-11-20 16:37+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -134,8 +134,8 @@ msgstr "Дочерний Бизнес Аккаунт" msgid "Child Business Accounts" msgstr "Дочерние Бизнес Аккаунты" -#: authentication/models/business_group.py:8 ml_model/models.py:18 -#: ml_model/models.py:38 ml_model/models.py:62 ml_model/models.py:269 +#: authentication/models/business_group.py:8 ml_model/models.py:17 +#: ml_model/models.py:37 ml_model/models.py:61 ml_model/models.py:268 #: payments/models/payment_plan.py:27 tools/chats/models.py:9 msgid "Title" msgstr "Название" @@ -162,7 +162,7 @@ msgid "Affiliated by" msgstr "Кем привлечена" #: authentication/models/business_host.py:36 authentication/models/user.py:138 -#: authentication/models/whitelist.py:16 ml_model/models.py:168 +#: authentication/models/whitelist.py:16 ml_model/models.py:167 #: payments/models/promocode.py:85 msgid "Is active" msgstr "Является активной" @@ -199,7 +199,7 @@ msgstr "ИНН" msgid "PSRN" msgstr "ОГРН" -#: authentication/models/business_host.py:69 ml_model/models.py:181 +#: authentication/models/business_host.py:69 ml_model/models.py:180 #: tools/public_api/models.py:30 msgid "Name" msgstr "Наименование" @@ -304,7 +304,7 @@ msgstr "Админ" msgid "Security" msgstr "Безопасность" -#: authentication/models/email_token.py:16 ml_model/models.py:271 +#: authentication/models/email_token.py:16 ml_model/models.py:270 msgid "Key" msgstr "Ключ" @@ -577,7 +577,7 @@ msgstr "Файл не может быть размером больше %(max_mb msgid "Version %(version)s already has input with the same type: %(type)s" msgstr "Версия %(version)s уже имеет входные данные с таким же типом: %(type)s" -#: ml_model/apps.py:9 ml_model/models.py:149 +#: ml_model/apps.py:9 ml_model/models.py:148 msgid "Neuron Models" msgstr "Нейронные Модели" @@ -591,6 +591,7 @@ msgid "Your request was blocked by our moderation system" msgstr "Ваш запрос был заблокирован нашей системой модерации" #: ml_model/exceptions.py:33 +#, python-format msgid "" "Image size %(cw)dx%(ch)d is not supported. Please rotate image to " "%(rw)dx%(rh)d" @@ -599,6 +600,7 @@ msgstr "" "до %(rw)dx%(rh)d" #: ml_model/exceptions.py:37 +#, python-format msgid "Image size %(cw)sx%(ch)s is not supported. Required size: %(rw)sx%(rh)s" msgstr "" "Размер изображения %(cw)sx%(ch)s не поддерживается. Требуемый размер: " @@ -633,286 +635,291 @@ msgstr "При рендеринге шаблона произошла неизв msgid "The neuron model does not exist" msgstr "Нейронная модель не существует" -#: ml_model/models.py:19 ml_model/models.py:39 ml_model/models.py:71 -#: ml_model/models.py:183 +#: ml_model/exceptions.py:80 +#, python-format +msgid "The %(file_type)s is not attached" +msgstr "Файл (%(file_type)s) не прикреплен" + +#: ml_model/models.py:18 ml_model/models.py:38 ml_model/models.py:70 +#: ml_model/models.py:182 msgid "Slug" msgstr "Ярлык" -#: ml_model/models.py:29 ml_model/models.py:81 +#: ml_model/models.py:28 ml_model/models.py:80 msgid "Category" msgstr "Категория" -#: ml_model/models.py:30 +#: ml_model/models.py:29 msgid "Categories" msgstr "Категории" -#: ml_model/models.py:43 +#: ml_model/models.py:42 msgid "Not SVG-pictures not allowed" msgstr "Нельзя использовать не SVG-картинки" -#: ml_model/models.py:46 +#: ml_model/models.py:45 msgid "Icon" msgstr "Миниатюра" -#: ml_model/models.py:53 +#: ml_model/models.py:52 msgid "Model Tag" msgstr "Тег модели" -#: ml_model/models.py:54 +#: ml_model/models.py:53 msgid "Model Tags" msgstr "Теги модели" -#: ml_model/models.py:67 +#: ml_model/models.py:66 msgid "Alternative Titles" msgstr "Альтернативные названия" -#: ml_model/models.py:69 ml_model/models.py:182 ml_model/models.py:270 +#: ml_model/models.py:68 ml_model/models.py:181 ml_model/models.py:269 #: payments/models/payment.py:52 msgid "Description" msgstr "Описание" -#: ml_model/models.py:73 +#: ml_model/models.py:72 msgid "Fill automatically, don't touch" msgstr "Заполняется автоматически, не трогать" -#: ml_model/models.py:89 +#: ml_model/models.py:88 msgid "Avatar" msgstr "Аватар" -#: ml_model/models.py:92 +#: ml_model/models.py:91 msgid "Tags" msgstr "Теги" -#: ml_model/models.py:148 +#: ml_model/models.py:147 msgid "Neuron Model" msgstr "Нейронная Модель" -#: ml_model/models.py:157 ml_model/models.py:403 payments/admin.py:95 +#: ml_model/models.py:156 ml_model/models.py:402 payments/admin.py:95 msgid "Model" msgstr "Модель" -#: ml_model/models.py:173 ml_model/models.py:174 +#: ml_model/models.py:172 ml_model/models.py:173 msgid "Settings" msgstr "Настройки" -#: ml_model/models.py:177 +#: ml_model/models.py:176 #, python-format msgid "Settings of %(model_title)s" msgstr "Настройки %(model_title)s" -#: ml_model/models.py:196 +#: ml_model/models.py:195 #, python-format msgid "%(model_title)s | %(version_name)s" msgstr "%(model_title)s | %(version_name)s" -#: ml_model/models.py:202 +#: ml_model/models.py:201 msgid "Model Version" msgstr "Версия Модели" -#: ml_model/models.py:203 +#: ml_model/models.py:202 msgid "Model Versions" msgstr "Версии Модели" -#: ml_model/models.py:212 +#: ml_model/models.py:211 msgid "Versions" msgstr "Версии" -#: ml_model/models.py:213 +#: ml_model/models.py:212 msgid "Link to versions" msgstr "Привязка к версиям" -#: ml_model/models.py:222 reports/models/error_report.py:10 +#: ml_model/models.py:221 reports/models/error_report.py:10 msgid "Text" msgstr "Текст" -#: ml_model/models.py:223 +#: ml_model/models.py:222 msgid "Image" msgstr "Картинка" -#: ml_model/models.py:224 +#: ml_model/models.py:223 msgid "PDF" msgstr "PDF" -#: ml_model/models.py:225 +#: ml_model/models.py:224 msgid "DOCX" msgstr "DOCX" -#: ml_model/models.py:226 +#: ml_model/models.py:225 msgid "DOC" msgstr "DOC" -#: ml_model/models.py:227 +#: ml_model/models.py:226 msgid "Text File (Notebook)" msgstr "Текстовый файл (Блокнот)" -#: ml_model/models.py:228 +#: ml_model/models.py:227 msgid "ZIP Archive" msgstr "ZIP архив" -#: ml_model/models.py:229 +#: ml_model/models.py:228 msgid "Audio" msgstr "Аудио" -#: ml_model/models.py:235 ml_model/models.py:273 +#: ml_model/models.py:234 ml_model/models.py:272 #: payments/models/promocode.py:41 msgid "Type" msgstr "Тип" -#: ml_model/models.py:237 ml_model/models.py:284 +#: ml_model/models.py:236 ml_model/models.py:283 msgid "Required" msgstr "Обязательный" -#: ml_model/models.py:240 +#: ml_model/models.py:239 #, python-format msgid "%(model_title)s | %(input_type)s" msgstr "%(model_title)s | %(input_type)s" -#: ml_model/models.py:246 +#: ml_model/models.py:245 msgid "Model Input" msgstr "Модель" -#: ml_model/models.py:247 +#: ml_model/models.py:246 msgid "Model Inputs" msgstr "Входящий поток модели" -#: ml_model/models.py:252 +#: ml_model/models.py:251 msgid "Integer" msgstr "Целое число" -#: ml_model/models.py:253 +#: ml_model/models.py:252 msgid "Float" msgstr "Вещественное число" -#: ml_model/models.py:254 +#: ml_model/models.py:253 msgid "String" msgstr "Строка" -#: ml_model/models.py:257 +#: ml_model/models.py:256 msgid "List" msgstr "Список" -#: ml_model/models.py:261 +#: ml_model/models.py:260 msgid "Float range" msgstr "Вещественный диапазон" -#: ml_model/models.py:265 +#: ml_model/models.py:264 msgid "Integer range" msgstr "Целочисленный диапазон" -#: ml_model/models.py:267 +#: ml_model/models.py:266 msgid "Logical" msgstr "Логический" -#: ml_model/models.py:280 +#: ml_model/models.py:279 msgid "Values" msgstr "Значения" -#: ml_model/models.py:281 +#: ml_model/models.py:280 msgid "" "These values can contain different interfaces and default value optional" msgstr "" "Значения могут содержать различные интерфейс и, опционально, значение по " "умолчанию" -#: ml_model/models.py:283 +#: ml_model/models.py:282 msgid "Hidden" msgstr "Скрытый" -#: ml_model/models.py:289 +#: ml_model/models.py:288 #, python-format msgid "Parameter of %(model_title)s" msgstr "Параметр %(model_title)s" -#: ml_model/models.py:292 +#: ml_model/models.py:291 msgid "Parameter" msgstr "Параметр" -#: ml_model/models.py:293 +#: ml_model/models.py:292 msgid "Parameters" msgstr "Параметры" -#: ml_model/models.py:298 +#: ml_model/models.py:297 msgid "Fixed" msgstr "Фикса" -#: ml_model/models.py:299 +#: ml_model/models.py:298 msgid "Per generation second" msgstr "За секунду генерации" -#: ml_model/models.py:300 +#: ml_model/models.py:299 msgid "Per one text token" msgstr "За один текстовый токен" -#: ml_model/models.py:301 +#: ml_model/models.py:300 msgid "Per image pixel" msgstr "За один пиксель" -#: ml_model/models.py:304 +#: ml_model/models.py:303 msgid "By input data" msgstr "По входящим данным" -#: ml_model/models.py:305 +#: ml_model/models.py:304 msgid "By output data" msgstr "По исходящим данным" -#: ml_model/models.py:306 +#: ml_model/models.py:305 msgid "By all data" msgstr "По всем данным" -#: ml_model/models.py:311 +#: ml_model/models.py:310 msgid "Strategy" msgstr "Стратегия" -#: ml_model/models.py:316 +#: ml_model/models.py:315 msgid "Interaction Type" msgstr "Тип взаимодействия" -#: ml_model/models.py:321 payments/models/invoice.py:19 +#: ml_model/models.py:320 payments/models/invoice.py:19 msgid "Cost" msgstr "Цена" -#: ml_model/models.py:322 +#: ml_model/models.py:321 msgid "In RUB, per specified strategy" msgstr "В рублях, за указанную стратегию" -#: ml_model/models.py:327 +#: ml_model/models.py:326 msgid "Coefficient" msgstr "Коэффициент" -#: ml_model/models.py:328 +#: ml_model/models.py:327 msgid "Cost multiplier" msgstr "Цена" -#: ml_model/models.py:335 +#: ml_model/models.py:334 msgid "Rate" msgstr "Ставка" -#: ml_model/models.py:339 +#: ml_model/models.py:338 msgid "Payment Rule" msgstr "Платежное правило" -#: ml_model/models.py:340 +#: ml_model/models.py:339 msgid "Payment Rules" msgstr "Платежные правила" -#: ml_model/models.py:401 +#: ml_model/models.py:400 msgid "Descriptor" msgstr "Дескриптор" -#: ml_model/models.py:407 +#: ml_model/models.py:406 #, python-format msgid "Instruction of %(model_title)s" msgstr "Инструкция %(model_title)s" -#: ml_model/models.py:410 +#: ml_model/models.py:409 msgid "Model Instruction" msgstr "Инструкция Модели" -#: ml_model/models.py:411 +#: ml_model/models.py:410 msgid "Model Instructions" msgstr "Инструкции Моделей" -#: ml_model/selectors/ml_models_selector.py:106 +#: ml_model/selectors/ml_models_selector.py:122 msgid "no model by this id" msgstr "Не найдено моделей по этому ID" @@ -1169,7 +1176,7 @@ msgstr "Публичный API" msgid "Media" msgstr "Медиа" -#: tools/chats/apis.py:178 tools/media/apis.py:161 +#: tools/chats/apis.py:181 tools/media/apis.py:160 #: tools/public_api/views/base.py:103 msgid "" "Error occured when create generation. It may cause NSFW-content not allowed, " @@ -30,6 +30,7 @@ from ml_model.services.perplexity import Perplexity from ml_model.services.pulid import Pulid from ml_model.services.qwen import Qwen from ml_model.services.raifgpt import Raifgpt +from ml_model.services.runway import Runway from ml_model.services.ray import Ray from ml_model.services.recraft import Recraft from ml_model.services.sdxlemoji import Sdxlemoji @@ -0,0 +1,67 @@ +import base64 +import time +from datetime import timedelta +from decimal import Decimal +from io import BytesIO + +import filetype +import requests +from django.core.files import File + +from messages.models import Message +from ml_model.exceptions import FileNotProvided +from ml_model.services.base import SimpleService +from ml_model.tasks import replicate_run + +from payments.exceptions.insufficient_balance import InsufficientBalance +from payments.selectors.payment_plan_selector import PaymentPlanSelector + + +class Runway(SimpleService): + + TOKENS_COST = { + 'gen4-turbo': Decimal('15'), + } + + def calculate_price(self, version: str, duration: int) -> Decimal: + price = self.TOKENS_COST[version] * duration + return price.quantize(Decimal('0.1'), rounding='ROUND_UP') + + def save_results(self, content: str, t: timedelta, video: str, save: bool = True) -> list[Message]: + msg = Message( + content=content, + content_object=self.store, + elapsed_time=t, + file=File(BytesIO(requests.get(video).content), '.mp4'), + ) + if save: + return Message.objects.bulk_create([msg]) + return [msg] + + def make(self, input_message: Message, save: bool = True) -> list[Message]: + version = input_message.info.pop('version', 'gen4-turbo') + duration = input_message.info.get('duration', 5) + file = input_message.file + if ( + (balance := PaymentPlanSelector(self.store.user).get_current_balance()) + < (cost := self.calculate_price(version, duration)) + ): + raise InsufficientBalance(balance, cost) + if not file: + raise FileNotProvided('Image') + kind = filetype.guess(file.read(100)) + mime = kind.mime if kind else 'application/octet-stream' + file.seek(0) + media = f'data:{mime};base64,{base64.b64encode(file.read()).decode("utf-8")}' + file.close() + callback_data = { + 'prompt': self.translate_prompt(input_message.content), + **input_message.info, + 'image': media + } + start_time = time.time() + video = replicate_run(f'runwayml/{version}', callback_data) + process_time = timedelta(seconds=(time.time() - start_time)) + self.handle_invoice(input_message.content_object.model, duration=duration, version=version) + msgs = self.save_results(input_message.content, process_time, video, save) + return msgs @@ -70,3 +70,11 @@ class TemplateUnknownException(Exception): class NeuronModelNotExist(Exception): def __str__(self): return _('The neuron model does not exist') + + +class FileNotProvided(Exception): + def __init__(self, file_type: str) -> None: + self.file_type = file_type + + def __str__(self) -> str: + return _('The %(file_type)s is not attached') % {'file_type': _(self.file_type).lower()} @@ -10,7 +10,7 @@ from rest_framework.views import APIView from messages.models import Message from messages.serializers import MessageSerializer -from ml_model.exceptions import RequestBlocked, UnsupportedSize +from ml_model.exceptions import RequestBlocked, UnsupportedSize, FileNotProvided from ml_model.models import NeuronModel from ml_model.services.base import SimpleService from payments.exceptions.insufficient_balance import InsufficientBalance @@ -152,7 +152,7 @@ class MediaAPIView(APIView): input_message.save() if isinstance(exc, InsufficientBalance): return Response({'detail': f'{exc}'}, status=HTTP_402_PAYMENT_REQUIRED) - if isinstance(exc, (UnsupportedSize, RequestBlocked)): + if isinstance(exc, (UnsupportedSize, RequestBlocked, FileNotProvided)): return Response({'detail': f'{exc}'}, status=HTTP_400_BAD_REQUEST) return Response( {