@@ -10,6 +10,7 @@ from ml_model.services.flux import Flux from ml_model.services.fluxproultra import Fluxproultra from ml_model.services.fluxlorafast import Fluxlorafast from ml_model.services.gemini import Gemini +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.iconic import Iconic @@ -0,0 +1,146 @@ +import base64 +import logging +import time + +from django.core.files import File +from datetime import timedelta +from decimal import Decimal +from io import BytesIO + +import httpx +from backend import settings +from messages.models import BaseStore, Message +from ml_model.models import ( + NeuronModel +) +from ml_model.services.base import SimpleService +from poller.models import Proxy + + +class Gptimage(SimpleService): + """ + GPTImage Service + contains abstract method make, which makes a generation + """ + + TOKENS_COST = { + 'gpt-image-1': { + 'input': Decimal('0.0025'), + 'image': { + 'input': Decimal('0.005'), + 'output': Decimal('0.02'), + 'generation': { + 'low': { + '1024x1024': Decimal('5.5'), + '1024x1536': Decimal('8'), + '1536x1024': Decimal('8'), + }, + 'medium': { + '1024x1024': Decimal('21'), + '1024x1536': Decimal('31.5'), + '1536x1024': Decimal('31.5'), + }, + 'high': { + '1024x1024': Decimal('83.5'), + '1024x1536': Decimal('125'), + '1536x1024': Decimal('125'), + } + } + } + } + } + + def __init__(self, store: BaseStore) -> None: + super().__init__(store) + self.logger = logging.getLogger(self.__class__.__name__) + + @property + def neuron_model(self): + return NeuronModel.objects.get(title='GPTImage') + + def make( + self, + input_message: Message, + save: bool = True, + ) -> list[Message]: + info = input_message.info.copy() + version = info.get('version', 'image-generate') + files = None + for proxy in Proxy.objects.all(): + json_data = { + 'prompt': input_message.content, + 'background': info.get('background'), + 'model': 'gpt-image-1', + 'output_format': 'png', + 'quality': info.get('quality'), + 'size': info.get('size') + } + if version == 'image-edit': + file_bytes = input_message.file.read() + files = { + 'image': ('image.png', BytesIO(file_bytes), 'image/png'), + } + with httpx.Client( + base_url='https://api.openai.com/v1', + proxy=f'{proxy.protocol}://{proxy.address}', + headers={'Authorization': f'Bearer {settings.OPENAI_API_KEY}'}, + timeout=600, + ) as client: + start_time = time.time() + if version == 'image-generate': + data = client.post( + 'images/generations', + json=json_data, + ).json() + else: + data = client.post( + 'images/edits', + data=json_data, + files=files + ).json() + process_time = timedelta(seconds=(time.time() - start_time)) + image = data['data'][0]['b64_json'] + text_tokens = data['usage']['input_tokens_details']['text_tokens'] + image_tokens = data['usage']['input_tokens_details']['image_tokens'] + output_tokens = data['usage']['output_tokens'] + self.logger.info(f'Input количество токенов: {text_tokens}; {image_tokens}') + self.logger.info(f'Output количество токенов: {output_tokens}') + self.handle_invoice(input_message.content_object.model, text_tokens, image_tokens, output_tokens) + msgs = self.save_results(input_message.content, image, process_time, save) + return msgs + + def calculate_price( + self, + text_tokens: int, + image_tokens: int, + output_tokens: int, + *args, + **kwargs, + ) -> Decimal: + price = ( + text_tokens * self.TOKENS_COST['gpt-image-1']['input'] + + image_tokens * self.TOKENS_COST['gpt-image-1']['image']['input'] + + output_tokens * self.TOKENS_COST['gpt-image-1']['image']['output'] + ) + return price.quantize(Decimal('0.1'), rounding='ROUND_UP') + + def save_results( + self, + prompt: str, + image: bytes, + time: timedelta, + save: bool = True, + ) -> list[Message]: + messages: list[Message] = [] + image_bytes = base64.b64decode(image) + messages.append( + Message( + content_object=self.store, + elapsed_time=time, + content=prompt, + file=File(BytesIO(image_bytes), '.png'), + ) + ) + if save: + return Message.objects.bulk_create(messages) + return messages