@@ -13,10 +13,12 @@ from ml_model.services.fluxkrea import Fluxkrea from ml_model.services.fluxlorafast import Fluxlorafast from ml_model.services.fluxproultra import Fluxproultra from ml_model.services.gemini import Gemini +from ml_model.services.gemma import Gemma from ml_model.services.geminiimage import Geminiimage from ml_model.services.gptimage import Gptimage from ml_model.services.granite import Granite from ml_model.services.grok import Grok +from ml_model.services.grok_4_1_fast import Grok_4_1_Fast from ml_model.services.hailuo import Hailuo from ml_model.services.hunyuan import Hunyuan from ml_model.services.iconic import Iconic @@ -0,0 +1,109 @@ +import base64 +import time +from datetime import timedelta +from decimal import Decimal +from io import BytesIO + +import filetype +from PIL import Image + +from messages.models import Message +from ml_model.services.EmbeddingService import EmbeddingService +from ml_model.services.FileService import FileProcessingService +from ml_model.services.base import SimpleService +from ml_model.tasks import openrouter_run +from poller.models import Proxy +from tools.chats.models import Chat +from tools.copywrite.models import Copywrite +from tools.public_api.models import APIStore + + +class Gemma(SimpleService): + TOKENS_COST = { + 'input': Decimal('20'), + 'output': Decimal('40'), + } + + def calculate_price(self, input_tokens: int, output_tokens: int) -> Decimal: + price = ( + input_tokens * self.TOKENS_COST['input'] / 1_000_000 + + output_tokens * self.TOKENS_COST['output'] / 1_000_000 + ) + return price.quantize(Decimal('0.01'), rounding='ROUND_UP') + + def save_results(self, content: str, t: timedelta, save: bool = True) -> list[Message]: + msgs = [ + Message( + content=content, + content_object=self.store, + elapsed_time=t, + ) + ] + if save: + return Message.objects.bulk_create(msgs) + return msgs + + def make(self, input_message: Message, save: bool = True) -> list[Message]: + callback_data = { + 'provider': {'order': ['DeepInfra']}, + **input_message.info, + } + messages = self.get_chat_history() + messages.append({'role': 'user', 'content': input_message.content}) + if input_message.file: + kind = filetype.guess(input_message.file.read(20)) + mime = kind.mime if kind else 'application/octet-stream' + input_message.file.seek(0) + image_url = f'data:{mime};base64,{base64.b64encode(input_message.file.read()).decode("utf-8")}' + input_message.file.close() + messages[-1]['content'] = [ + {'type': 'text', 'text': input_message.content}, + {'type': 'image_url', 'image_url': {'url': image_url}}, + ] + start_time = time.time() + result = openrouter_run('google/gemma-3-4b-it', messages, callback_data, 'Gemma') + process_time = timedelta(seconds=(time.time() - start_time)) + self.handle_invoice( + input_message.content_object.model, + input_tokens=result[1], + output_tokens=result[2], + ) + msgs = self.save_results(result[0], process_time) + return msgs + + def get_chat_history( + self, message_limit: int = 10, max_character_limit: int = 1500 + ) -> list[dict[str, str | list]]: + if isinstance(self.store, Chat): + air_messages = list( + reversed( + Message.objects.filter( + chats_chats_messages=self.store, is_deleted=False, is_sent=True + ).order_by('-created_at')[1 : message_limit + 1] + ) + ) + elif isinstance(self.store, APIStore): + air_messages = [] + elif isinstance(self.store, Copywrite): + air_messages = list( + reversed( + Message.objects.filter( + copywrite_copywrites_messages=self.store, + is_deleted=False, + is_sent=True, + ).order_by('-created_at')[:message_limit] + ) + ) + else: + air_messages = [] + memory = [] + for msg in air_messages: + content = msg.content or '' + if msg.from_model: + memory.append({'role': 'assistant', 'content': content}) + else: + memory.append({'role': 'user', 'content': content}) + character_length = sum(len(content['content']) for content in memory) + while character_length > max_character_limit: + character_length -= len(memory.pop(0)['content']) + return memory @@ -0,0 +1,101 @@ +import base64 +import time +from datetime import timedelta +from decimal import Decimal +from typing import Any, Iterator + +import filetype + +from messages.models import Message +from ml_model.services.base import SimpleService +from ml_model.tasks import openrouter_run +from tools.chats.models import Chat +from tools.copywrite.models import Copywrite +from tools.public_api.models import APIStore + + +class Grok_4_1_Fast(SimpleService): + TOKENS_COST = {'input': Decimal('40'), 'output': Decimal('100')} + + def calculate_price(self, input_tokens: int, output_tokens: int) -> Decimal: + price = ( + input_tokens * self.TOKENS_COST['input'] / 1_000_000 + + output_tokens * self.TOKENS_COST['output'] / 1_000_000 + ) + return price.quantize(Decimal('0.01'), rounding='ROUND_UP') + + def save_results(self, content: Iterator[Any], t: timedelta, save: bool = True) -> list[Message]: + msgs = [ + Message( + content=content, + content_object=self.store, + elapsed_time=t, + ) + ] + if save: + return Message.objects.bulk_create(msgs) + return msgs + + def make(self, input_message: Message, save: bool = True) -> list[Message]: + callback_data = { + 'provider': {'order': ['xAI']}, + **input_message.info, + } + messages = self.get_chat_history() + messages.append({'role': 'user', 'content': input_message.content}) + if input_message.file: + kind = filetype.guess(input_message.file.read(20)) + mime = kind.mime if kind else 'application/octet-stream' + input_message.file.seek(0) + image_url = f'data:{mime};base64,{base64.b64encode(input_message.file.read()).decode("utf-8")}' + input_message.file.close() + messages[-1]['content'] = [ + {'type': 'text', 'text': input_message.content}, + {'type': 'image_url', 'image_url': {'url': image_url}}, + ] + start_time = time.time() + result = openrouter_run('x-ai/grok-4.1-fast', messages, callback_data, 'Grok 4.1 Fast') + process_time = timedelta(seconds=(time.time() - start_time)) + self.handle_invoice( + input_message.content_object.model, + input_tokens=result[1], + output_tokens=result[2], + ) + msgs = self.save_results(result[0], process_time) + return msgs + + def get_chat_history( + self, message_limit: int = 10, max_character_limit: int = 1500 + ) -> list[dict[str, str | list]]: + if isinstance(self.store, Chat): + air_messages = list( + reversed( + Message.objects.filter( + chats_chats_messages=self.store, is_deleted=False, is_sent=True + ).order_by('-created_at')[1 : message_limit + 1] + ) + ) + elif isinstance(self.store, APIStore): + air_messages = [] + elif isinstance(self.store, Copywrite): + air_messages = list( + reversed( + Message.objects.filter( + copywrite_copywrites_messages=self.store, + is_deleted=False, + is_sent=True, + ).order_by('-created_at')[:message_limit] + ) + ) + memory = [] + for msg in air_messages: + content = msg.content or '' + if msg.from_model: + memory.append({'role': 'assistant', 'content': content}) + else: + memory.append({'role': 'user', 'content': content}) + character_length = sum(len(content['content']) for content in memory) + while character_length > max_character_limit: + character_length -= len(memory.pop(0)['content']) + + return memory @@ -1,4 +1,3 @@ -import base64 import time from datetime import timedelta from decimal import Decimal @@ -11,43 +10,37 @@ from replicate.exceptions import ModelError from messages.models import Message from ml_model.exceptions import RequestBlocked, GenerationException -from ml_model.models import ( - NeuronModel, -) from ml_model.services.base import SimpleService from ml_model.tasks import replicate_run class Leonardo(SimpleService): - """ - Flux Service - contains abstract method make, which makes a generation - """ - TOKENS_COST = { 'lucid-origin': { - 'input_imgs': Decimal('1.5'), - }, # 1k images + 'input_units': Decimal('450'), + } } _CALLBACK_BASE = 'leonardoai/' - def calculate_price(self, input_message: Message, version: str) -> Decimal: - price_map = self.TOKENS_COST[version] - price = price_map['input_imgs'] / 1_000 - if num_images := input_message.info.get('num_images'): - price = price * num_images + def calculate_price(self, version: str, num_images: int, generation_mode: str) -> Decimal: + image_prices = {'standard': 18, 'ultra': 51} + price = ( + Decimal(f'{self.TOKENS_COST[version]["input_units"] / 1000 * image_prices[generation_mode]}') + * num_images + ) return price.quantize(Decimal('0.1'), rounding='ROUND_UP') @classmethod def predict_price(cls, content: str, file_exists: bool, info: dict[str, Any]) -> Decimal | None: - price = cls.TOKENS_COST['lucid-origin']['input_imgs'] / 1_000 * info['num_images'] + version = info.get('version', 'lucid-origin') + num_images = info.get('num_images', 1) + generation_mode = info.get('generation_mode', 'standard') + image_prices = {'standard': 18, 'ultra': 51} + input_units = cls.TOKENS_COST[version]['input_units'] + price = (input_units / 1000 * image_prices[generation_mode]) * num_images return price.quantize(Decimal('0.1'), rounding='ROUND_UP') - @property - def neuron_model(self): - return NeuronModel.objects.get(title='Flux') - def save_results( self, prompt: str, @@ -71,7 +64,9 @@ class Leonardo(SimpleService): def make(self, input_message: Message, save: bool = True) -> list[Message]: start_time = time.time() - version = input_message.info.get('version') + version = input_message.info.get('version', 'lucid-origin') + generation_mode = input_message.info.get('generation_mode', 'standard') + num_images = input_message.info.get('num_images', 1) callback_data = dict( { 'prompt': self.translate_prompt(input_message.content), @@ -89,6 +84,11 @@ class Leonardo(SimpleService): raise GenerationException from exc images = runner if isinstance(runner, list) else [runner] process_time = timedelta(seconds=(time.time() - start_time)) - self.handle_invoice(input_message.content_object.model, input_message=input_message, version=version) + self.handle_invoice( + input_message.content_object.model, + version=version, + num_images=num_images, + generation_mode=generation_mode, + ) msgs = self.save_results(input_message.content, images, process_time, save) return msgs