@@ -16,7 +16,7 @@ router = Router(auth=AsyncAuthBearer(), tags=['ml_model']) @router.post('predict-price/', tags=['ml_model/predict-price'], response=PredictPriceSchema) def calculate_predict_price(request, body: PredictPriceInputSchema): payload = body.dict() - content = payload.pop('content') if body.model_slug != 'qwen_3_tts' else payload.get('content') + content = payload.pop('content') if body.model_slug != 'elevenlabs' else payload.get('content') json_str = json.dumps(payload, sort_keys=True, separators=(',', ':')) signature = hashlib.sha256(json_str.encode('utf-8')).hexdigest() cache_key = f'predict_price:{signature}' @@ -8,6 +8,7 @@ from ml_model.services.deepl import Deepl from ml_model.services.deepseek import Deepseek from ml_model.services.djourney import Djourney from ml_model.services.epicphotogasm import Epicphotogasm +from ml_model.services.elevenlabs_music import Elevenlabs_Music from ml_model.services.elevenlabs import Elevenlabs from ml_model.services.flux import Flux from ml_model.services.flux_2 import Flux_2 @@ -54,7 +55,6 @@ from ml_model.services.pruna_v import Pruna_V from ml_model.services.qwen import Qwen from ml_model.services.qwen_235B import Qwen_235B from ml_model.services.qwen_3_5 import Qwen_3_5 -from ml_model.services.qwen_3_tts import Qwen_3_Tts from ml_model.services.qwen_3_max_thinking import Qwen_3_Max_Thinking from ml_model.services.raifgpt import Raifgpt from ml_model.services.ray import Ray @@ -9,55 +9,57 @@ from django.core.files import File from replicate.exceptions import ModelError from messages.models import Message -from ml_model.exceptions import RequestBlocked, GenerationException +from ml_model.exceptions import GenerationException, RequestBlocked 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 Elevenlabs(SimpleService): - TOKENS_COST = Decimal('2.490') + TOKENS_PER_1K_CHARS = Decimal('6') @classmethod def predict_price(cls, content: str, file_exists: bool, info: dict[str, Any]) -> Decimal | None: - duration = info['duration'] - return (cls.TOKENS_COST * duration).quantize(Decimal('0.1'), rounding='ROUND_UP') + chars = len(content) + price = cls.TOKENS_PER_1K_CHARS * Decimal(chars) / Decimal(1000) + return price.quantize(Decimal('0.1'), rounding='ROUND_UP') - def calculate_price(self, duration: int) -> Decimal: - return (self.TOKENS_COST * duration).quantize(Decimal('0.1'), rounding='ROUND_UP') + def calculate_price(self, content: str) -> Decimal: + chars = len(content) + price = self.TOKENS_PER_1K_CHARS * Decimal(chars) / Decimal(1000) + return price.quantize(Decimal('0.1'), rounding='ROUND_UP') - def save_results(self, content: str, t: timedelta, video: str, save: bool = True) -> list[Message]: + def save_results(self, content: str, t: timedelta, audio_url: str, save: bool = True) -> list[Message]: msg = Message( content=content, content_object=self.store, elapsed_time=t, - file=File(BytesIO(requests.get(video).content), '.mp3'), + file=File(BytesIO(requests.get(audio_url).content), '.mp3'), ) if save: return Message.objects.bulk_create([msg]) return [msg] def make(self, input_message: Message, save: bool = True) -> list[Message]: - duration = input_message.info.pop('duration') - if (balance := PaymentPlanSelector(self.store.user).get_current_balance()) < ( - cost := self.calculate_price(duration) - ): + cost = self.calculate_price(input_message.content) + if (balance := PaymentPlanSelector(self.store.user).get_current_balance()) < cost: raise InsufficientBalance(balance, cost) callback_data = { - 'prompt': input_message.content, - 'music_length_ms': duration * 1000, - **input_message.info, + 'text': input_message.content, + 'mode': 'voice_clone', + 'reference_audio': input_message.file.url, } + if transcription := input_message.info.get('transcription', ''): + callback_data.update({'reference_text': transcription}) start_time = time.time() try: - audio = replicate_run('elevenlabs/music', callback_data) + result = replicate_run('qwen/qwen3-tts', callback_data) except ModelError as exc: if any(error in str(exc) for error in ('E005', 'E006', 'sexual')): raise RequestBlocked raise GenerationException from exc + process_time = timedelta(seconds=(time.time() - start_time)) - self.handle_invoice(input_message.content_object.model, duration=duration) - msgs = self.save_results(input_message.content, process_time, audio, save) - return msgs + self.handle_invoice(input_message.content_object.model, content=input_message.content) + return self.save_results(input_message.content, process_time, result, save) @@ -9,57 +9,55 @@ from django.core.files import File from replicate.exceptions import ModelError from messages.models import Message -from ml_model.exceptions import GenerationException, RequestBlocked +from ml_model.exceptions import RequestBlocked, GenerationException 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 Qwen_3_Tts(SimpleService): - TOKENS_PER_1K_CHARS = Decimal('6') +class Elevenlabs_Music(SimpleService): + TOKENS_COST = Decimal('2.490') @classmethod def predict_price(cls, content: str, file_exists: bool, info: dict[str, Any]) -> Decimal | None: - chars = len(content) - price = cls.TOKENS_PER_1K_CHARS * Decimal(chars) / Decimal(1000) - return price.quantize(Decimal('0.1'), rounding='ROUND_UP') + duration = info['duration'] + return (cls.TOKENS_COST * duration).quantize(Decimal('0.1'), rounding='ROUND_UP') - def calculate_price(self, content: str) -> Decimal: - chars = len(content) - price = self.TOKENS_PER_1K_CHARS * Decimal(chars) / Decimal(1000) - return price.quantize(Decimal('0.1'), rounding='ROUND_UP') + def calculate_price(self, duration: int) -> Decimal: + return (self.TOKENS_COST * duration).quantize(Decimal('0.1'), rounding='ROUND_UP') - def save_results(self, content: str, t: timedelta, audio_url: str, save: bool = True) -> list[Message]: + 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(audio_url).content), '.mp3'), + file=File(BytesIO(requests.get(video).content), '.mp3'), ) if save: return Message.objects.bulk_create([msg]) return [msg] def make(self, input_message: Message, save: bool = True) -> list[Message]: - cost = self.calculate_price(input_message.content) - if (balance := PaymentPlanSelector(self.store.user).get_current_balance()) < cost: + duration = input_message.info.pop('duration') + if (balance := PaymentPlanSelector(self.store.user).get_current_balance()) < ( + cost := self.calculate_price(duration) + ): raise InsufficientBalance(balance, cost) callback_data = { - 'text': input_message.content, - 'mode': 'voice_clone', - 'reference_audio': input_message.file.url, + 'prompt': input_message.content, + 'music_length_ms': duration * 1000, + **input_message.info, } - if transcription := input_message.info.get('transcription', ''): - callback_data.update({'reference_text': transcription}) start_time = time.time() try: - result = replicate_run('qwen/qwen3-tts', callback_data) + audio = replicate_run('elevenlabs/music', callback_data) except ModelError as exc: if any(error in str(exc) for error in ('E005', 'E006', 'sexual')): raise RequestBlocked raise GenerationException from exc - process_time = timedelta(seconds=(time.time() - start_time)) - self.handle_invoice(input_message.content_object.model, content=input_message.content) - return self.save_results(input_message.content, process_time, result, save) + self.handle_invoice(input_message.content_object.model, duration=duration) + msgs = self.save_results(input_message.content, process_time, audio, save) + return msgs