@@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-10 11:02+0300\n" +"POT-Creation-Date: 2026-08-13 17:52+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -766,7 +766,8 @@ msgstr "Промпт слишком длинный" 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" @@ -1097,17 +1098,21 @@ msgstr "Инструкции Моделей" msgid "no model by this id" msgstr "Не найдено моделей по этому ID" -#: ml_model/services/FileService.py:110 +#: ml_model/services/FileService.py:123 #: tools/public_api/views/providers/openai_compatible.py:208 msgid "Voice not found." msgstr "Голос не найден." +#: ml_model/services/FileService.py:227 +msgid "Image is too wide or tall" +msgstr "Изображение слишком широкое или высокое" + #: ml_model/services/chatgpt.py:245 msgid "Image is ready" msgstr "Изображение готово" #: ml_model/services/chatgpt.py:361 ml_model/services/claude.py:277 -#: ml_model/services/grok.py:191 +#: ml_model/services/grok.py:192 msgid "File analysis" msgstr "Анализ файлов" @@ -1134,14 +1139,6 @@ msgstr "1080р разрешение не поддерживается для See msgid "3K output is not supported for this model" msgstr "3К разрешение не поддерживается для этой модели" -#: ml_model/services/flux_3.py:87 -msgid "Draft mode is only available at 720p" -msgstr "Режим draft доступен только при 720p" - -#: ml_model/services/FileService.py -msgid "The attached video must be at most %(max_seconds)d seconds" -msgstr "Прикреплённое видео должно быть не длиннее %(max_seconds)d секунд" - #: ml_model/services/upscaleai.py:124 msgid "No image given for improving" msgstr "Нет изображения для улучшения" @@ -221,6 +221,13 @@ class ImageFileValidator(MediaFileValidator): if w * h > max_pixels: raise ImageTooLargeError(max_pixels) + @staticmethod + def validate_aspect_ratio(w: int | None, h: int | None, *, min_ratio: float, max_ratio: float) -> None: + if not (w and h): + raise CorruptedFileError + if not (min_ratio <= w / h <= max_ratio): + raise InvalidParameterError(_('Image is too wide or tall')) + class VideoFileValidator(MediaFileValidator): @staticmethod @@ -14,7 +14,6 @@ from ml_model.services.elevenlabs_music import Elevenlabs_Music from ml_model.services.epicphotogasm import Epicphotogasm from ml_model.services.flux import Flux from ml_model.services.flux_2 import Flux_2 -from ml_model.services.flux_3 import Flux_3 from ml_model.services.fluxkrea import Fluxkrea from ml_model.services.fluxlorafast import Fluxlorafast from ml_model.services.fluxproultra import Fluxproultra @@ -47,9 +47,9 @@ class Chatgpt(Chatgpt_4, StreamSimpleService, OpenAIStreamMixin): 'input': Decimal('0.0025'), # $5 / 1M tokens 'output': Decimal('0.015'), # $30 / 1M tokens 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call }, 'code_interpreter': Decimal('15'), # 1 call 'generated_image': Decimal('10.2'), @@ -58,31 +58,31 @@ class Chatgpt(Chatgpt_4, StreamSimpleService, OpenAIStreamMixin): 'input': Decimal('0.0025'), # $5 / 1M tokens 'output': Decimal('0.015'), # $30 / 1M tokens 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call }, 'code_interpreter': Decimal('15'), # 1 call 'generated_image': Decimal('10.2'), }, 'gpt-5.6-luna': { - 'input': Decimal('0.0001'), # $0.2 / 1M tokens - 'output': Decimal('0.0006'), # $1.2 / 1M tokens + 'input': Decimal('0.0005'), # $1 / 1M tokens + 'output': Decimal('0.003'), # $6 / 1M tokens 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call }, 'code_interpreter': Decimal('15'), # 1 call 'generated_image': Decimal('10.2'), }, 'gpt-5.6-terra': { - 'input': Decimal('0.001'), # $2 / 1M tokens - 'output': Decimal('0.006'), # $12 / 1M tokens + 'input': Decimal('0.00125'), # $2.5 / 1M tokens + 'output': Decimal('0.0075'), # $15 / 1M tokens 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call }, 'code_interpreter': Decimal('15'), # 1 call 'generated_image': Decimal('10.2'), @@ -493,9 +493,7 @@ class Chatgpt(Chatgpt_4, StreamSimpleService, OpenAIStreamMixin): Decimal(serper_sources * 250) / Decimal(2.7) * self.TOKENS_COST[model_name]['input'] ) if info.get('code_interpreter'): - json_data['tools'].append( - {'type': 'code_interpreter', 'container': {'type': 'auto', 'memory_limit': '1g'}} - ) + json_data['tools'].append({'type': 'code_interpreter', 'container': {'type': 'auto'}}) messages[-1]['content'] += ' the python tool ' predicted_input_price += self.TOKENS_COST[model_name]['code_interpreter'] if ctx['image']: @@ -67,18 +67,18 @@ class Chatgpt_4(SimpleService): 'input': Decimal('0.0003'), 'output': Decimal('0.0003'), 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('12.5'), # 1 call + 'medium': Decimal('13.75'), # 1 call + 'high': Decimal('15'), # 1 call }, }, 'gpt-4o': { 'input': Decimal('0.005'), 'output': Decimal('0.005'), 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('15'), # 1 call + 'medium': Decimal('17.5'), # 1 call + 'high': Decimal('25'), # 1 call }, }, 'gpt-oss-120b': {'input': Decimal('0.0002'), 'output': Decimal('0.0002')}, @@ -566,7 +566,7 @@ class Chatgpt_4(SimpleService): 'input': messages, 'tools': [ { - 'type': 'web_search', + 'type': 'web_search_preview', 'search_context_size': search_context_size, 'user_location': {'type': 'approximate', 'country': 'RU'}, } @@ -26,9 +26,9 @@ class Chatgpt_5(Chatgpt_4): 'input': Decimal('0.000625'), 'output': Decimal('0.005'), 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call }, 'code_interpreter': Decimal('15'), }, @@ -36,9 +36,9 @@ class Chatgpt_5(Chatgpt_4): 'input': Decimal('0.000125'), 'output': Decimal('0.001'), 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call }, 'code_interpreter': Decimal('15'), }, @@ -51,9 +51,9 @@ class Chatgpt_5(Chatgpt_4): 'input': Decimal('0.000625'), 'output': Decimal('0.005'), 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call }, 'code_interpreter': Decimal('15'), # 1 call }, @@ -61,9 +61,9 @@ class Chatgpt_5(Chatgpt_4): 'input': Decimal('0.0075'), 'output': Decimal('0.06'), 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call }, }, 'gpt-5.1-codex-max': { @@ -82,9 +82,9 @@ class Chatgpt_5(Chatgpt_4): 'input': Decimal('0.000875'), 'output': Decimal('0.007'), 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call }, 'code_interpreter': Decimal('15'), # 1 call }, @@ -239,9 +239,7 @@ class Chatgpt_5(Chatgpt_4): 'gpt-5', 'gpt-5.1', ): - json_data['tools'].append( - {'type': 'code_interpreter', 'container': {'type': 'auto', 'memory_limit': '1g'}} - ) + json_data['tools'].append({'type': 'code_interpreter', 'container': {'type': 'auto'}}) messages[-1]['content'] += 'the python tool' input_tokens, output_tokens, response = self.call_openai_api( proxy=proxy, endpoint='responses', json_data=json_data @@ -21,9 +21,9 @@ class Chatgpt_5_4(Chatgpt): 'input': Decimal('0.00125'), 'output': Decimal('0.0075'), 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), + 'medium': Decimal('5'), + 'high': Decimal('5'), }, 'code_interpreter': Decimal('15'), 'generated_image': Decimal('10.2'), @@ -32,9 +32,9 @@ class Chatgpt_5_4(Chatgpt): 'input': Decimal('0.0075'), 'output': Decimal('0.045'), 'web_search': { - 'low': Decimal('5'), # $0.01 / call - 'medium': Decimal('5'), # $0.01 / call - 'high': Decimal('5'), # $0.01 / call + 'low': Decimal('5'), + 'medium': Decimal('5'), + 'high': Decimal('5'), }, 'generated_image': Decimal('10.2'), }, @@ -227,11 +227,7 @@ class Claude(SerperMixin, StreamSimpleService): return memory def _build_callback_data(self, input_message: Message) -> dict[str, Any]: - return { - **input_message.info, - 'provider': {'order': ['anthropic'], 'allow_fallbacks': False}, - 'tools': [], - } + return {'provider': {'order': ['anthropic']}, **input_message.info, 'tools': []} def _prepare_messages( self, input_message: Message, version_slug: str, callback_data: dict @@ -26,12 +26,12 @@ class Deepseek(SimpleService): # 'output': Decimal('0'), # }, 'deepseek/deepseek-v4-pro': { - 'input': Decimal('261') / 1_000_000, # $0.87 / 1M tokens - 'output': Decimal('522') / 1_000_000, # $1.74 / 1M tokens + 'input': Decimal('1050') / 1_000_000, + 'output': Decimal('2200') / 1_000_000, }, - 'deepseek/deepseek-v4-flash-0731': { - 'input': Decimal('24') / 1_000_000, # $0.08 / 1M tokens - 'output': Decimal('75.6') / 1_000_000, # $0.252 / 1M tokens + 'deepseek/deepseek-v4-flash': { + 'input': Decimal('100') / 1_000_000, + 'output': Decimal('175') / 1_000_000, }, } @@ -62,7 +62,6 @@ class Deepseek(SimpleService): callback_data = { **info, - 'provider': {'order': ['digitalocean'], 'allow_fallbacks': False}, } messages = [ {'role': 'system', 'content': system_prompt}, @@ -1,119 +0,0 @@ -import time -from datetime import timedelta -from decimal import Decimal -from io import BytesIO -from typing import Any - -import requests -from django.core.files import File -from django.utils.translation import gettext as _ - -from messages.models import Message -from ml_model.exceptions import InvalidParameterError -from ml_model.services.FileService import ( - ImageFileProcessor, - MediaFileProcessor, - MediaFileValidator, - VideoFileProcessor, - VideoFileValidator, -) -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 Flux_3(SimpleService): - MAX_START_VIDEO_SECONDS = 15 - TOKENS_COST = { - 't2v_i2v': { - '720p': Decimal('51'), # $0.17 / sec - '1080p': Decimal('87'), # $0.29 / sec - 'draft': Decimal('18'), # $0.06 / sec - }, - 'v2v': { - '720p': Decimal('123'), # $0.41 / sec - '1080p': Decimal('159'), # $0.53 / sec - 'draft': Decimal('36'), # $0.12 / sec - }, - } - - @classmethod - def predict_price(cls, content: str, file_exists: bool, info: dict[str, Any]) -> Decimal | None: - if file_exists: - return None - resolution = info.get('resolution', '720p') - duration = int(info.get('duration', 5)) - draft = bool(info.get('draft', False)) - if resolution not in ('720p', '1080p'): - return None - if draft and resolution != '720p': - return None - key = 'draft' if draft else resolution - return (cls.TOKENS_COST['t2v_i2v'][key] * duration).quantize(Decimal('0.1'), rounding='ROUND_UP') - - def calculate_price(self, variant: str, resolution: str, duration: int, draft: bool) -> Decimal: - key = 'draft' if draft else resolution - return (self.TOKENS_COST[variant][key] * duration).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]: - resolution = input_message.info.pop('resolution', '720p') - duration = int(input_message.info.pop('duration', 5)) - draft = bool(input_message.info.get('draft', False)) - input_message.info.pop('safety_tolerance', None) - if draft and resolution != '720p': - raise InvalidParameterError(_('Draft mode is only available at 720p')) - variant = 't2v_i2v' - callback_data = dict( - { - 'prompt': input_message.content, - 'resolution': resolution, - 'duration': str(duration), - **input_message.info, - 'safety_tolerance': 2, - } - ) - if file := input_message.file: - processor = MediaFileProcessor(file) - kind = processor.get_kind(processor.get_bytes(50)) - allowed_extensions = ImageFileProcessor.EXTENSIONS + VideoFileProcessor.EXTENSIONS - MediaFileValidator.validate_kind(kind, allowed_extensions) - if kind.extension.upper() in VideoFileProcessor.EXTENSIONS: - video_processor = VideoFileProcessor(file) - VideoFileValidator.validate_duration( - video_processor.get_duration(), - max_seconds=self.MAX_START_VIDEO_SECONDS, - ) - callback_data.update({'start_video': file.url}) - variant = 'v2v' - else: - callback_data.update({'images': [file.url]}) - file.close() - if (balance := PaymentPlanSelector(self.store.user).get_current_balance()) < ( - predicted := self.calculate_price(variant, resolution, duration, draft) - ): - raise InsufficientBalance(balance, predicted) - start_time = time.time() - video = replicate_run('black-forest-labs/flux-3', callback_data) - process_time = timedelta(seconds=(time.time() - start_time)) - self.handle_invoice( - input_message.content_object.model, - variant=variant, - resolution=resolution, - duration=duration, - draft=draft, - ) - msgs = self.save_results(input_message.content, process_time, video, save) - return msgs @@ -99,8 +99,8 @@ class Gemini(SimpleService): raise ModelVersionNotAvailable(version_slug, self.TOKENS_COST) version = f'google/{version_slug}' callback_data = { + 'provider': {'order': ['Google AI Studio']}, **input_message.info, - 'provider': {'order': ['google-ai-studio'], 'allow_fallbacks': False}, } messages = self.get_chat_history() messages.append({'role': 'user', 'content': input_message.content}) @@ -79,8 +79,8 @@ class Gemini_3_1(StreamSimpleService): raise ModelVersionNotAvailable(version_slug, self.TOKENS_COST) model_slug = f'google/{version_slug}:online' callback_data = { + 'provider': {'order': ['Google AI Studio']}, **input_message.info, - 'provider': {'order': ['google-ai-studio'], 'allow_fallbacks': False}, } messages, embedding_tokens = self._prepare_messages(input_message) @@ -103,8 +103,8 @@ class Gemini_3_1(StreamSimpleService): raise ModelVersionNotAvailable(version_slug, self.TOKENS_COST) model_slug = f'google/{version_slug}:online' callback_data = { + 'provider': {'order': ['Google AI Studio']}, **input_message.info, - 'provider': {'order': ['google-ai-studio'], 'allow_fallbacks': False}, } messages, embedding_tokens = self._prepare_messages(input_message) input_tokens = output_tokens = 0 @@ -41,8 +41,8 @@ class Gemma(SimpleService): def make(self, input_message: Message, save: bool = True) -> list[Message]: callback_data = { + 'provider': {'order': ['DeepInfra']}, **input_message.info, - 'provider': {'order': ['deepinfra'], 'allow_fallbacks': False}, } messages = self.get_chat_history() messages.append({'role': 'user', 'content': input_message.content}) @@ -53,8 +53,8 @@ class Grok_4_1_Fast(SimpleService): def make(self, input_message: Message, save: bool = True) -> list[Message]: callback_data = { + 'provider': {'order': ['xAI']}, **input_message.info, - 'provider': {'order': ['xai'], 'allow_fallbacks': False}, } messages = self.get_chat_history() messages.append({'role': 'user', 'content': input_message.content}) @@ -1,17 +1,15 @@ -import base64 import time from datetime import timedelta from decimal import Decimal from io import BytesIO from typing import Any -import filetype import requests from django.core.files import File - from messages.models import Message -from ml_model.exceptions import RequestBlocked, GenerationException, FileNotProvided +from ml_model.exceptions import FileNotProvided +from ml_model.services.FileService import ImageFileProcessor, ImageFileValidator from ml_model.services.base import SimpleService from ml_model.tasks import replicate_run @@ -47,17 +45,24 @@ class Kling(SimpleService): def make(self, input_message: Message, save: bool = True) -> list[Message]: mode = input_message.info.pop('mode', 'standard') duration = input_message.info.get('duration', 5) - if (balance := PaymentPlanSelector(self.store.user).get_current_balance()) < (cost := self.TOKENS_COST[mode] * duration): + if (balance := PaymentPlanSelector(self.store.user).get_current_balance()) < ( + cost := self.TOKENS_COST[mode] * duration + ): raise InsufficientBalance(balance, cost) if not input_message.file: raise FileNotProvided('Image') - callback_data = dict({'prompt': self.translate_prompt(input_message.content), 'mode': mode, **input_message.info}) - kind = filetype.guess(input_message.file.read(20)) - mime = kind.mime if kind else 'application/octet-stream' - input_message.file.seek(0) - image = f'data:{mime};base64,{base64.b64encode(input_message.file.read()).decode("utf-8")}' + callback_data = dict( + {'prompt': self.translate_prompt(input_message.content), 'mode': mode, **input_message.info} + ) + + image_processor = ImageFileProcessor(input_message.file) + kind = image_processor.get_kind(image_processor.get_bytes(20)) + ImageFileValidator.validate_kind(kind, image_processor.EXTENSIONS) + width, height = image_processor.get_dimensions() + ImageFileValidator.validate_dimensions(width, height, max_pixels=36_000_000) + ImageFileValidator.validate_aspect_ratio(width, height, min_ratio=0.40, max_ratio=2.50) input_message.file.close() - callback_data.update({'start_image': image}) + callback_data.update({'start_image': input_message.file.url}) start_time = time.time() video = replicate_run('kwaivgi/kling-v2.1', callback_data) process_time = timedelta(seconds=(time.time() - start_time)) @@ -66,10 +66,7 @@ class Llama(SimpleService): if version_slug is None or version_slug not in self.TOKENS_COST: raise ModelVersionNotAvailable(version_slug, self.TOKENS_COST) version = f'meta-llama/{version_slug}' - callback_data = { - **input_message.info, - 'provider': {'order': ['deepinfra'], 'allow_fallbacks': False}, - } + callback_data = {'provider': {'order': ['DeepInfra']}, **input_message.info} messages = self.get_chat_history() messages.append({'role': 'user', 'content': input_message.content}) image = input_message.file @@ -17,9 +17,9 @@ from payments.selectors.payment_plan_selector import PaymentPlanSelector class Ltx(SimpleService): TOKENS_COST = { - '1080p': Decimal('18'), # $0.06 / sec - '2k': Decimal('36'), # $0.12 / sec - '4k': Decimal('72'), # $0.24 / sec + '1080p': Decimal('12'), + '2k': Decimal('24'), + '4k': Decimal('48'), } @classmethod @@ -55,10 +55,7 @@ class Mistral(SimpleService): def make(self, input_message: Message, save: bool = True) -> list[Message]: version = 'mistralai/mistral-small-3.1-24b-instruct' - callback_data = { - **input_message.info, - 'provider': {'order': ['parasail'], 'allow_fallbacks': False}, - } + callback_data = {'provider': {'order': ['Parasail']}, **input_message.info} messages = self.get_chat_history() messages.append({'role': 'user', 'content': input_message.content}) image = input_message.file @@ -69,10 +69,7 @@ class Perplexity(SimpleService): if version_slug is None or version_slug not in self.TOKENS_COST: raise ModelVersionNotAvailable(version_slug, self.TOKENS_COST) version = f'perplexity/{version_slug}' - callback_data = { - **input_message.info, - 'provider': {'order': ['perplexity'], 'allow_fallbacks': False}, - } + callback_data = {'provider': {'order': ['Perplexity']}, **input_message.info} messages = self.get_chat_history() messages.append({'role': 'user', 'content': input_message.content}) try: @@ -54,10 +54,7 @@ class Qwen(SimpleService): if version_slug is None or version_slug not in self.TOKENS_COST: raise ModelVersionNotAvailable(version_slug, self.TOKENS_COST) version = f'qwen/{version_slug}' - callback_data = { - **input_message.info, - 'provider': {'order': ['deepinfra'], 'allow_fallbacks': False}, - } + callback_data = {'provider': {'order': ['DeepInfra']}, **input_message.info} messages = self.get_chat_history() messages.insert( 0, @@ -58,10 +58,7 @@ class Qwen_235B(SimpleService): if version_slug is None or version_slug not in self.TOKENS_COST: raise ModelVersionNotAvailable(version_slug, self.TOKENS_COST) version = f'qwen/{version_slug}' - callback_data = { - **input_message.info, - 'provider': {'order': ['deepinfra'], 'allow_fallbacks': False}, - } + callback_data = {'provider': {'order': ['DeepInfra']}, **input_message.info} messages = self.get_chat_history() messages.insert( 0, @@ -26,8 +26,8 @@ class Qwen_3_7(StreamSimpleService): COEFFICIENT = Decimal('300.0') TOKENS_COST = { - 'qwen3.7-max': {'input': Decimal('442.5'), 'output': Decimal('1327.5')}, # $1.475 / $4.425 - 'qwen3.7-plus': {'input': Decimal('96'), 'output': Decimal('384')}, # $0.32 / $1.28 + 'qwen3.7-max': {'input': Decimal('750'), 'output': Decimal('2250')}, + 'qwen3.7-plus': {'input': Decimal('120'), 'output': Decimal('480')}, } MAX_OUTPUT_TOKENS = 30_000 @@ -38,10 +38,7 @@ class Qwen_3_Max_Thinking(SimpleService): def make(self, input_message: Message, save: bool = True) -> list[Message]: start_time = time.time() - callback_data = { - **input_message.info, - 'provider': {'order': ['alibaba'], 'allow_fallbacks': False}, - } + callback_data = {'provider': {'order': ['alibaba']}, **input_message.info} messages = self.get_chat_history() messages.append({'role': 'user', 'content': input_message.content}) result = openrouter_run('qwen/qwen3-max-thinking:online', messages, callback_data, 'Qwen') @@ -20,12 +20,12 @@ from ml_model.tasks import bytedance_model_ark_run class Reve(SimpleService): # Reve временно не работает на репликейте. Временно используем сидрим - TEMPORARY_PROVIDER_MODEL = 'seedream-5-0-lite-260128' + TEMPORARY_PROVIDER_MODEL = 'seedream-5-0-260128' PRICE = { - '2K': Decimal('17.5'), # $0.035 / image (seedream-5-0-lite-260128) - '3K': Decimal('17.5'), # $0.035 / image - '4K': Decimal('17.5'), # $0.035 / image + '2K': Decimal('25'), + '3K': Decimal('50'), + '4K': Decimal('100'), } # PRICE = { # 'create': Decimal('12.5'), @@ -12,7 +12,7 @@ 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 ImageFileProcessor, ImageFileValidator +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 @@ -22,18 +22,18 @@ from payments.selectors.payment_plan_selector import PaymentPlanSelector class Seedream(SimpleService): TOKEN_COST = { 'seedream-boosted': { - '2K': Decimal('17.5'), # $0.035 / image - '3K': Decimal('17.5'), # $0.035 / image - '4K': Decimal('17.5'), # $0.035 / image + '2K': Decimal('25'), + '3K': Decimal('50'), + '4K': Decimal('100'), }, 'seedream-4.5': { - '2K': Decimal('20'), # $0.04 / image - '4K': Decimal('20'), # $0.04 / image + '2K': Decimal('25'), + '4K': Decimal('100'), }, } VERSION_MAPPING = { - 'seedream-boosted': 'seedream-5-0-lite-260128', + 'seedream-boosted': 'seedream-5-0-260128', 'seedream-4.5': 'seedream-4-5-251128', } @@ -94,12 +94,13 @@ class Seedream(SimpleService): **input_message.info, } if image := input_message.file: - image_processor = ImageFileProcessor(image) - kind = image_processor.get_kind(image_processor.get_bytes(20)) - ImageFileValidator.validate_kind(kind, image_processor.EXTENSIONS) + image_processor = ImageFileProcessingService(image) + file_bytes = image_processor.get_bytes(20) + kind = image_processor.validate_kind(image_processor.get_kind(file_bytes)) + image_processor.validate_extension(kind) width, height = image_processor.get_dimensions() - ImageFileValidator.validate_dimensions(width, height, max_pixels=36000000) - image.close() + image_processor.validate_dimensions(width, height, max_pixels=36_000_000) + image_processor.image.close() callback_data.update({'image': image.url}) images = bytedance_model_ark_run( @@ -3,4 +3,4 @@ from ml_model.services import Minimaxmusic class Suno(Minimaxmusic): - TOKENS_COST = Decimal('15') # $0.03 * 100 * 5 + TOKENS_COST = Decimal('17.5')