@@ -1,14 +0,0 @@ -from django.utils.translation import gettext_lazy as _ -from rest_framework.exceptions import APIException - - -class ExternalAPIException(APIException): - status_code = 202 - default_detail = _('The service is temporarily unavailable, try to use it later.') - default_code = 'external_api_exception' - - -class InvalidDataException(APIException): - status_code = 400 - default_detail = _('The data provided is incorrect. Please check and try again.') - default_code = 'invalid_data' @@ -1,5 +1,5 @@ -import itertools import base64 +import itertools import logging import subprocess import time @@ -14,7 +14,6 @@ import docx2txt import filetype import httpx import tiktoken -from django.conf import settings from django.core.files.uploadedfile import UploadedFile from langchain import hub from langchain.agents import AgentExecutor, create_structured_chat_agent @@ -29,9 +28,9 @@ from langchain_text_splitters import RecursiveCharacterTextSplitter from PIL import Image from PyPDF2 import PdfReader -from backend import settings +from messages.models import BaseStore, Message from ml_model.constants import TEMPORARY_TEST_TEXT -from messages.models import Message, BaseStore +from ml_model.exceptions import GenerationException from ml_model.models import ( ModelCategory, ModelConfiguration, @@ -124,6 +123,7 @@ class Chatgpt(SimpleService): file = input_message.file image = None image_size = None + normalized_image = None if file: file_extension = Path(file.name).suffix if file_extension == '.pdf': @@ -146,195 +146,189 @@ class Chatgpt(SimpleService): buf.close() image_size = normalized_image.size input_content.append({'type': 'image_url', 'image_url': {'url': image_url}}) - self.llm = ChatOpenAI( - model=model_name, - openai_api_base=f'http://{settings.OPENAI_PROXY_HOST}?' - + '&'.join( - f'proxies={proxy.protocol}://{proxy.address}' - for proxy in Proxy.objects.all() - ) - + f'&token={settings.OPENAI_API_KEY}' - + '&uri=', - default_headers={'X-Authorization': 'proxypassapiairfail'}, - ) - if model_name in ( - 'o1-preview', - 'o1-mini', - ): - self.llm.temperature = 1 - self.llm.model_kwargs = { - 'presence_penalty': info.pop('presence_penalty', 0), - 'top_p': info.pop('top_p', 1), - } - if info.get('use_web'): - del info['use_web'] - else: - self.llm.temperature = info.pop('temperature', 0.5) - self.llm.model_kwargs = { - 'presence_penalty': info.pop('presence', 0), - 'top_p': info.pop('top_p', 0.5), - } - if model_name in ( - 'gpt-4', - 'gpt-4o', - 'gpt-4o-mini', - 'o1-preview', - 'o1-mini', - 'o3-mini', - ): - self.llm.tiktoken_model_name = 'gpt-4' - chat_history = self.get_chat_history() - conversation = RunnableWithMessageHistory( - runnable=self.llm, - get_session_history= lambda _: self.get_chat_history().chat_memory - ) - llm_input = HumanMessage(content=input_content) - if file and not image: - input_tokens = self.count_text_tokens( - [*chat_history.buffer_as_messages, llm_input, *chunks] + for proxy in Proxy.objects.all(): + self.llm = ChatOpenAI( + model=model_name, + http_client=httpx.Client(proxy=f'{proxy.protocol}://{proxy.address}'), ) - elif image: - input_tokens = self.count_text_tokens( - [llm_input] + if model_name in ( + 'o1-preview', + 'o1-mini', + ): + self.llm.temperature = 1 + self.llm.model_kwargs = { + 'presence_penalty': info.pop('presence_penalty', 0), + 'top_p': info.pop('top_p', 1), + } + if info.get('use_web'): + del info['use_web'] + else: + self.llm.temperature = info.pop('temperature', 0.5) + self.llm.model_kwargs = { + 'presence_penalty': info.pop('presence', 0), + 'top_p': info.pop('top_p', 0.5), + } + if model_name in ( + 'gpt-4', + 'gpt-4o', + 'gpt-4o-mini', + 'o1-preview', + 'o1-mini', + 'o3-mini', + ): + self.llm.tiktoken_model_name = 'gpt-4' + chat_history = self.get_chat_history() + conversation = RunnableWithMessageHistory( + runnable=self.llm, + get_session_history=lambda _: self.get_chat_history().chat_memory, ) - else: - input_tokens = self.count_text_tokens( - [*chat_history.buffer_as_messages, llm_input] + llm_input = HumanMessage(content=input_content) + if file and not image: + input_tokens = self.count_text_tokens( + [*chat_history.buffer_as_messages, llm_input, *chunks] + ) + elif image: + input_tokens = self.count_text_tokens([llm_input]) + else: + input_tokens = self.count_text_tokens( + [*chat_history.buffer_as_messages, llm_input] + ) + self.assert_enough_balance( + input_tokens, image_size, model=self.llm.model_name ) - self.assert_enough_balance(input_tokens, image_size, model=self.llm.model_name) - if image: - response = self.llm.invoke([llm_input]) - chat_history.chat_memory.add_ai_message(response) - elif file: - human_messages = [] - chunk_responses = ['Содержание файла: '] - for chunk in chunks: - prompt = [ - { - 'type': 'text', - 'text': f'Сгенерируй 2-3 предложения, которые суммирует следующий текст. ' - f'Включи основную мысль текста и все значимые числовые данные. Текст: {chunk}', - } - ] - human_message = HumanMessage(content=prompt) - human_messages.append(human_message) + if image: + response = self.llm.invoke([llm_input]) + chat_history.chat_memory.add_ai_message(response) + elif file: + human_messages = [] + chunk_responses = ['Содержание файла: '] + for chunk in chunks: + prompt = [ + { + 'type': 'text', + 'text': f'Сгенерируй 2-3 предложения, которые суммирует следующий текст. ' + f'Включи основную мысль текста и все значимые числовые данные. Текст: {chunk}', + } + ] + human_message = HumanMessage(content=prompt) + human_messages.append(human_message) + response = conversation.invoke( + {'input': human_message.content[0]['text']}, + config={'configurable': {'session_id': 'default'}}, + ) + chunk_responses.append(response.content) + combined_summary = ' '.join(chunk_responses) + user_prompt = ( + input_message.content + if input_message.content.split() + else 'Суммируй текст' + ) + question_content = ( + f'Вот краткое содержание каждого чанка:\n' + f'{combined_summary}\nОтветьте на вопрос по содержанию файла: {user_prompt}' + ) + input = HumanMessage(content=question_content) + input_tokens = self.count_text_tokens(human_messages + [input]) response = conversation.invoke( {'input': human_message.content[0]['text']}, - config={"configurable": {"session_id": "default"}} + config={'configurable': {'session_id': 'default'}}, ) - chunk_responses.append(response.content) - combined_summary = ' '.join(chunk_responses) - user_prompt = input_message.content if input_message.content.split() else 'Суммируй текст' - question_content = ( - f'Вот краткое содержание каждого чанка:\n' - f'{combined_summary}\nОтветьте на вопрос по содержанию файла: {user_prompt}' - ) - input = HumanMessage(content=question_content) - input_tokens = self.count_text_tokens(human_messages + [input]) - response = conversation.invoke( - {'input': human_message.content[0]['text']}, - config={"configurable": {"session_id": "default"}} - ) - elif info.get('use_web', False): - prompt_schema = hub.pull('hwchase17/structured-chat-agent') - tools = [GoogleSerperResults()] - agent = create_structured_chat_agent(self.llm, tools, prompt_schema) - agent_executor = AgentExecutor( - agent=agent, - tools=tools, - handle_parsing_errors=True, - max_iterations=10, - max_execution_time=45, - ) - response = AIMessage( - content=agent_executor.invoke( - { - 'input': [llm_input], - 'chat_history': chat_history.buffer_as_messages - + [ - SystemMessage( - content='Учитывай язык диалога перед выдачей ответа' - ), - SystemMessage( - content='Никому не говори, что ты не можешь найти информацию в интернете' - ), - ], - } - )['output'] - ) - elif model_name == 'o3-mini': - with httpx.Client( - base_url=f'http://{settings.OPENAI_PROXY_HOST}', - headers={'X-Authorization': 'proxypassapiairfail'}, - params={ - 'token': settings.OPENAI_API_KEY, - 'proxies': [ - f'{proxy.protocol}://{proxy.address}' - for proxy in Proxy.objects.all() - ], - 'uri': 'chat/completions', - }, - timeout=None, - ) as client: - resp = client.post( - '', - json={ - 'model': model_name, - 'messages': [ - {'role': 'user', 'content': input_message.content}, - ], - }, + elif info.get('use_web', False): + prompt_schema = hub.pull('hwchase17/structured-chat-agent') + tools = [GoogleSerperResults()] + agent = create_structured_chat_agent(self.llm, tools, prompt_schema) + agent_executor = AgentExecutor( + agent=agent, + tools=tools, + handle_parsing_errors=True, + max_iterations=10, + max_execution_time=45, ) - if ( - (data := resp.json()) - and data.get('choices') - and ( - content := ','.join( - [ - choice['message']['content'] - for choice in data.get('choices') - ] - ) + response = AIMessage( + content=agent_executor.invoke( + { + 'input': [llm_input], + 'chat_history': chat_history.buffer_as_messages + + [ + SystemMessage( + content='Учитывай язык диалога перед выдачей ответа' + ), + SystemMessage( + content='Никому не говори, что ты бот и не можешь найти информацию в интернете' + ), + ], + } + )['output'] + ) + elif model_name == 'o3-mini': + with httpx.Client( + base_url='https://openai.com', + proxy=f'{proxy.protocol}://{proxy.address}', + ) as client: + resp = client.post( + 'chat/completions', + json={ + 'model': model_name, + 'messages': [ + {'role': 'user', 'content': input_message.content}, + ], + }, ) - ): - response = AIMessage(content=content) - else: - raise Exception('GPT not answer correctly, please retry later') - else: - # Somehow this chain doesn't support Vision, even though ChatOpenAI (above) does. - response = conversation.invoke( - {'input': llm_input.content[0]['text']}, - config={"configurable": {"session_id": "default"}} - ) - chat_history.chat_memory.add_ai_message(response) - process_time = timedelta(seconds=time.time() - start_time) + if ( + (data := resp.json()) + and data.get('choices') + and ( + content := ','.join( + [ + choice['message']['content'] + for choice in data.get('choices') + ] + ) + ) + ): + response = AIMessage(content=content) + else: + raise Exception('GPT not answer correctly, please retry later') + else: + # Somehow this chain doesn't support Vision, even though ChatOpenAI (above) does. + response = conversation.invoke( + {'input': llm_input.content[0]['text']}, + config={'configurable': {'session_id': 'default'}}, + ) + chat_history.chat_memory.add_ai_message(response) - output_tokens = self.count_text_tokens([response]) - if file and not image: - output_tokens += self.count_text_tokens( - [AIMessage(chunk_response) for chunk_response in chunk_responses] - ) + output_tokens = self.count_text_tokens([response]) + if file and not image: + output_tokens += self.count_text_tokens( + [AIMessage(chunk_response) for chunk_response in chunk_responses] + ) + + if image and normalized_image: + self.logger.info( + f'Input количество токенов БЕЗ картинки {model_name} - {input_tokens}' + ) + input_tokens += self.count_image_tokens(normalized_image.size, model_name) - if image: self.logger.info( - f'Input количество токенов БЕЗ картинки {model_name} - {input_tokens}' + f'Input количество токенов для {model_name} - {input_tokens}' + ) + self.logger.info( + f'Output количество токенов для {model_name} - {output_tokens}' + ) + self.logger.info( + f'Общее количество токенов для {model_name} - {input_tokens + output_tokens}' ) - input_tokens += self.count_image_tokens(normalized_image.size, model_name) - - self.logger.info(f'Input количество токенов для {model_name} - {input_tokens}') - self.logger.info(f'Output количество токенов для {model_name} - {output_tokens}') - self.logger.info( - f'Общее количество токенов для {model_name} - {input_tokens + output_tokens}' - ) - self.handle_invoice( - self.neuron_model, - input_tokens, - output_tokens, - self.llm.model_name, - ) - msgs = self.save_results([response], process_time, save) - return msgs + process_time = timedelta(seconds=time.time() - start_time) + self.handle_invoice( + self.neuron_model, + input_tokens, + output_tokens, + self.llm.model_name, + ) + msgs = self.save_results([response], process_time, save) + return msgs + raise GenerationException def get_chat_history( self, @@ -1,13 +1,11 @@ import time - -import requests from datetime import timedelta from decimal import Decimal +import requests from django.conf import settings -from messages.models import Message, BaseStore -from ml_model.exceptions.external_api import ExternalAPIException +from messages.models import BaseStore, Message from ml_model.models import ModelCategory, ModelInput, ModelParameter from ml_model.services.base import SimpleService @@ -17,6 +15,7 @@ class Granite(SimpleService): Granite-3.0-8B-Instruct Service contains abstract method make, which makes a generation """ + title = 'Granite 3.0' description = 'Нейросеть, способная генерировать качественный текст из вашего промпта' category = ModelCategory(title='Чат-боты', slug='chat-bots') @@ -27,7 +26,7 @@ class Granite(SimpleService): name='Системный промпт', key='system_prompt', type=ModelParameter.TypeChoices.STR, - hidden=True + hidden=True, ), ModelParameter( name='Лучший процент', @@ -45,7 +44,7 @@ class Granite(SimpleService): TOKEN_PAYMENT_RULES = { 'granite-input': Decimal('27.5'), # 1M tokens - 'granite-output': Decimal('137.5') # 1M tokens + 'granite-output': Decimal('137.5'), # 1M tokens } def __init__(self, store: BaseStore) -> None: @@ -62,34 +61,40 @@ class Granite(SimpleService): } data = {'input': payload} response = requests.post( - url=f'{self.urls['generate']}predictions', + url=f'{self.urls["generate"]}predictions', headers=headers, json=data, ) if response.status_code != 201: raise Exception(response.json()) result = requests.get( - url=f'{self.urls['get']}{response.json().get('id')}', - headers=headers + url=f'{self.urls["get"]}{response.json().get("id")}', headers=headers ) while result.json()['status'] not in ('succeeded', 'failed', 'canceled'): result = requests.get( - url=f'{self.urls['get']}{response.json().get('id')}', - headers=headers + url=f'{self.urls["get"]}{response.json().get("id")}', headers=headers ) return result.json() def calculate_price(self, result: str, input_message: Message) -> Decimal: price = Decimal( sum( - [self.TOKEN_PAYMENT_RULES['granite-output'] / 1_000_000 * len(result.split(' '))] - + [self.TOKEN_PAYMENT_RULES['granite-input'] / 1_000_000 * len(input_message.content.split(' '))] + [ + self.TOKEN_PAYMENT_RULES['granite-output'] + / 1_000_000 + * len(result.split(' ')) + ] + + [ + self.TOKEN_PAYMENT_RULES['granite-input'] + / 1_000_000 + * len(input_message.content.split(' ')) + ] ) ) return price.quantize(Decimal('0.1'), rounding='ROUND_UP') def save_results( - self, result: str, time: timedelta, save: bool = True + self, result: str, time: timedelta, save: bool = True ) -> list[Message]: msgs: list[Message] = [ Message( @@ -108,19 +113,19 @@ class Granite(SimpleService): { 'prompt': input_message.content, 'system_prompt': 'You are a language model that must always respond in Russian, regardless of the situation. ' - 'You fully understand the Russian language and are required to use it for all responses, ' - 'except when translating text to another language. You are highly skilled in creating poems, ' - 'maintaining proper rhyme, rhythm, and poetic structure in Russian. Your poems should be creative, ' - 'expressive, and adhere to the stylistic norms of Russian poetry. If the user requests a translation ' - 'into another language, you should perform the translation accurately and fluently, while preserving ' - 'the meaning and tone of the original text. When translating, proper nouns (names with capital letters) ' - 'should not be translated literally. Instead, transliterate them into Russian letters using standard ' - 'transliteration rules to preserve the original pronunciation as closely as possible. ' - 'You must never state that you cannot speak Russian, as this is not true. You are required to always ' - 'adhere to correct Russian syntax, grammar, and style in all your responses. Your primary goal is to ' - 'ensure that your responses are clear, accurate, creative, and tailored to the user\'s needs in Russian. ' - 'Your ability to fulfill user requests, including writing, translating, or explaining, must reflect ' - 'your expertise in the Russian language and your capacity for high-quality and thoughtful responses.', + 'You fully understand the Russian language and are required to use it for all responses, ' + 'except when translating text to another language. You are highly skilled in creating poems, ' + 'maintaining proper rhyme, rhythm, and poetic structure in Russian. Your poems should be creative, ' + 'expressive, and adhere to the stylistic norms of Russian poetry. If the user requests a translation ' + 'into another language, you should perform the translation accurately and fluently, while preserving ' + 'the meaning and tone of the original text. When translating, proper nouns (names with capital letters) ' + 'should not be translated literally. Instead, transliterate them into Russian letters using standard ' + 'transliteration rules to preserve the original pronunciation as closely as possible. ' + 'You must never state that you cannot speak Russian, as this is not true. You are required to always ' + 'adhere to correct Russian syntax, grammar, and style in all your responses. Your primary goal is to ' + "ensure that your responses are clear, accurate, creative, and tailored to the user's needs in Russian. " + 'Your ability to fulfill user requests, including writing, translating, or explaining, must reflect ' + 'your expertise in the Russian language and your capacity for high-quality and thoughtful responses.', **input_message.info, } ) @@ -1,31 +1,30 @@ import time - -import requests from datetime import timedelta from decimal import Decimal from io import BytesIO +import requests +from django.core.files import File + from messages.models import Message -from ml_model.exceptions.external_api import ExternalAPIException from ml_model.models import ModelCategory, ModelInput, ModelParameter from ml_model.services.base import SimpleService from ml_model.tasks import replicate_run -from django.core.files import File - class Iconic(SimpleService): """ Iconic Service contains abstract method make, which makes a generation """ + title = 'Iconic' description = 'Нейросеть, способная генерировать картинки из вашего текста' category = ModelCategory(title='Изображения', slug='images') versions = [] inputs = [ ModelInput(type=ModelInput.TypeChoices.TEXT, required=True), - ModelInput(type=ModelInput.TypeChoices.IMAGE) + ModelInput(type=ModelInput.TypeChoices.IMAGE), ] parameters = [ ModelParameter( @@ -75,7 +74,7 @@ class Iconic(SimpleService): '4:3', '9:16', '9:21', - 'custom' + 'custom', ], 'default': '1:1', }, @@ -112,7 +111,7 @@ class Iconic(SimpleService): return price.quantize(Decimal('0.1'), rounding='ROUND_UP') def save_results( - self, prompt: str, images: list, time: timedelta, save: bool = True + self, prompt: str, images: list, time: timedelta, save: bool = True ) -> list[Message]: messages: list[Message] = [] for image in images: @@ -121,7 +120,7 @@ class Iconic(SimpleService): content_object=self.store, elapsed_time=time, content=prompt, - file=File(BytesIO(requests.get(image).content), '.png') + file=File(BytesIO(requests.get(image).content), '.png'), ) ) if save: @@ -7,7 +7,6 @@ import requests from django.core.files import File from messages.models import Message -from ml_model.exceptions.external_api import ExternalAPIException from ml_model.models import ( ModelCategory, ModelInput, @@ -22,6 +21,7 @@ class Lightning(SimpleService): Lightning Service contains abstract method make, which makes a generation """ + title = 'Lightning' description = 'Нейросеть, способная генерировать картинки из вашего текста' category = ModelCategory(title='Изображения', slug='images') @@ -58,7 +58,7 @@ class Lightning(SimpleService): 'K_EULER_ANCESTRAL', 'K_EULER', 'PNDM', - 'DPM++2MSDE' + 'DPM++2MSDE', ], 'default': 'K_EULER', }, @@ -67,7 +67,7 @@ class Lightning(SimpleService): name='Количество изображений', key='num_outputs', type=ModelParameter.TypeChoices.INTRANGE, - values={'start': 1, 'end': 4, 'step': 1, 'default': 1} + values={'start': 1, 'end': 4, 'step': 1, 'default': 1}, ), ModelParameter( name='Точность запроса', @@ -95,7 +95,7 @@ class Lightning(SimpleService): return price.quantize(Decimal('0.1'), rounding='ROUND_UP') def save_results( - self, prompt: str, images: list, time: timedelta, save: bool = True + self, prompt: str, images: list, time: timedelta, save: bool = True ) -> list[Message]: messages: list[Message] = [] for image in images: @@ -104,7 +104,7 @@ class Lightning(SimpleService): content_object=self.store, elapsed_time=time, content=prompt, - file=File(BytesIO(requests.get(image).content), '.png') + file=File(BytesIO(requests.get(image).content), '.png'), ) ) if save: @@ -1,32 +1,30 @@ import time - -import requests from datetime import timedelta from decimal import Decimal -from replicate.exceptions import ReplicateError, ModelError from io import BytesIO +import requests +from django.core.files import File + from messages.models import Message -from ml_model.exceptions.external_api import ExternalAPIException, InvalidDataException from ml_model.models import ModelCategory, ModelInput, ModelParameter from ml_model.services.base import SimpleService from ml_model.tasks import replicate_run -from django.core.files import File - class Logoai(SimpleService): """ Logo AI Service contains abstract method make, which makes a generation """ + title = 'Logo AI' description = 'Нейросеть, способная генерировать фотографии из вашего текста' category = ModelCategory(title='Изображения', slug='images') versions = [] inputs = [ ModelInput(type=ModelInput.TypeChoices.TEXT, required=True), - ModelInput(type=ModelInput.TypeChoices.IMAGE) + ModelInput(type=ModelInput.TypeChoices.IMAGE), ] parameters = [ ModelParameter( @@ -58,7 +56,7 @@ class Logoai(SimpleService): 'KarrasDPM', 'K_EULER_ANCESTRAL', 'K_EULER', - 'PNDM' + 'PNDM', ], 'default': 'K_EULER', }, @@ -95,7 +93,7 @@ class Logoai(SimpleService): return price.quantize(Decimal('0.1'), rounding='ROUND_UP') def save_results( - self, prompt: str, images: list, time: timedelta, save: bool = True + self, prompt: str, images: list, time: timedelta, save: bool = True ) -> list[Message]: messages: list[Message] = [] for image in images: @@ -104,7 +102,7 @@ class Logoai(SimpleService): content_object=self.store, elapsed_time=time, content=prompt, - file=File(BytesIO(requests.get(image).content), '.png') + file=File(BytesIO(requests.get(image).content), '.png'), ) ) if save: @@ -1,31 +1,30 @@ import time - -import requests from datetime import timedelta from decimal import Decimal from io import BytesIO +import requests +from django.core.files import File + from messages.models import Message -from ml_model.exceptions.external_api import ExternalAPIException from ml_model.models import ModelCategory, ModelInput, ModelParameter from ml_model.services.base import SimpleService from ml_model.tasks import replicate_run -from django.core.files import File - class Pulid(SimpleService): """ PuLID Service contains abstract method make, which makes a generation """ + title = 'PuLID' description = 'Нейросеть, способная генерировать фотографии из вашего текста' category = ModelCategory(title='Изображения', slug='images') versions = [] inputs = [ ModelInput(type=ModelInput.TypeChoices.TEXT), - ModelInput(type=ModelInput.TypeChoices.IMAGE, required=True) + ModelInput(type=ModelInput.TypeChoices.IMAGE, required=True), ] parameters = [ ModelParameter( @@ -92,8 +91,7 @@ class Pulid(SimpleService): PRICE = Decimal('0.374') _CALLBACK = ( - 'zsxkib/pulid' - ':43d309c37ab4e62361e5e29b8e9e867fb2dcbcec77ae91206a8d95ac5dd451a0' + 'zsxkib/pulid:43d309c37ab4e62361e5e29b8e9e867fb2dcbcec77ae91206a8d95ac5dd451a0' ) def calculate_price(self, process_time: timedelta) -> Decimal: @@ -101,7 +99,7 @@ class Pulid(SimpleService): return price.quantize(Decimal('0.1'), rounding='ROUND_UP') def save_results( - self, prompt: str, images: list, time: timedelta, save: bool = True + self, prompt: str, images: list, time: timedelta, save: bool = True ) -> list[Message]: messages: list[Message] = [] for image in images: @@ -110,7 +108,7 @@ class Pulid(SimpleService): content_object=self.store, elapsed_time=time, content=prompt, - file=File(BytesIO(requests.get(image).content), '.png') + file=File(BytesIO(requests.get(image).content), '.png'), ) ) if save: @@ -134,7 +132,7 @@ class Pulid(SimpleService): 'or partially rendered eyes, deformed eyeballs, cross-eyed, blurry, udity, partial' 'nudity, suggestive poses, revealing clothing, explicit content, offensive symbols, ' 'provocative expressions, graphic violence, inappropriate themes' - f'{input_message.info.pop('negative_prompt', '')}' + f'{input_message.info.pop("negative_prompt", "")}' ), **input_message.info, } @@ -1,31 +1,30 @@ import time - -import requests from datetime import timedelta from decimal import Decimal from io import BytesIO +import requests +from django.core.files import File + from messages.models import Message -from ml_model.exceptions.external_api import ExternalAPIException from ml_model.models import ModelCategory, ModelInput, ModelParameter from ml_model.services.base import SimpleService from ml_model.tasks import replicate_run -from django.core.files import File - class Sdxlemoji(SimpleService): """ Sdxl-emoji Service contains abstract method make, which makes a generation """ + title = 'Sdxl-emoji' description = 'Нейросеть, способная генерировать фотографии из вашего текста' category = ModelCategory(title='Изображения', slug='images') versions = [] inputs = [ ModelInput(type=ModelInput.TypeChoices.TEXT, required=True), - ModelInput(type=ModelInput.TypeChoices.IMAGE) + ModelInput(type=ModelInput.TypeChoices.IMAGE), ] parameters = [ ModelParameter( @@ -63,7 +62,7 @@ class Sdxlemoji(SimpleService): 'KarrasDPM', 'K_EULER_ANCESTRAL', 'K_EULER', - 'PNDM' + 'PNDM', ], 'default': 'K_EULER', }, @@ -85,8 +84,7 @@ class Sdxlemoji(SimpleService): PRICE = Decimal('0.529') _CALLBACK = ( - 'fofr/sdxl-emoji' - ':dee76b5afde21b0f01ed7925f0665b7e879c50ee718c5f78a9d38e04d523cc5e' + 'fofr/sdxl-emoji:dee76b5afde21b0f01ed7925f0665b7e879c50ee718c5f78a9d38e04d523cc5e' ) def calculate_price(self, process_time: timedelta) -> Decimal: @@ -94,7 +92,7 @@ class Sdxlemoji(SimpleService): return price.quantize(Decimal('0.1'), rounding='ROUND_UP') def save_results( - self, prompt: str, images: list, time: timedelta, save: bool = True + self, prompt: str, images: list, time: timedelta, save: bool = True ) -> list[Message]: messages: list[Message] = [] for image in images: @@ -103,7 +101,7 @@ class Sdxlemoji(SimpleService): content_object=self.store, elapsed_time=time, content=prompt, - file=File(BytesIO(requests.get(image).content), '.png') + file=File(BytesIO(requests.get(image).content), '.png'), ) ) if save: @@ -0,0 +1,12 @@ +# накинуть перевод через gettext_lazy + + +class GenerationException(Exception): + def __str__(self): + return 'Случилась ошибка во время генерации у этой модели, пожалуйста повторите попытку позже' + + +class NSFWDetectedException(Exception): ... + + +class LargeResourceConsumptionException(Exception): ... @@ -142,10 +142,8 @@ class MessagesAPIView(APIView): except Exception as exc: input_message.is_sent = False input_message.save() - logger.info(f'Error occured: {exc}') - return Response( - f'Error occured: {exc}', status=400 - ) + logger.exception(exc) + return Response(f'Error occured: {exc}', status=400) output_messages.insert(0, input_message) return Response(MessageSerializer(output_messages, many=True).data, 201) else: @@ -9,10 +9,11 @@ services: command: - /bin/sh - -c - - python manage.py initialize_buckets && - python manage.py migrate && - python manage.py collectstatic --no-input && - (python manage.py createsuperuser --no-input || true) && + - | + python manage.py initialize_buckets + python manage.py migrate + python manage.py collectstatic --no-input + (python manage.py createsuperuser --no-input || true) python -m uvicorn --host 0.0.0.0 --workers 1 --timeout-keep-alive 300 --ws wsproto --loop uvloop --http httptools --lifespan off backend.asgi:application --log-level debug --reload volumes: - .:/code @@ -1,3 +1,5 @@ +# temporary remove python manage.py migrate for safety moving to cluster + services: app: image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA @@ -42,24 +44,6 @@ services: - traefik.http.middlewares.backend.redirectscheme.permanent=true env_file: - $ENV - depends_on: - migrator: - condition: service_completed_successfully - cache-mdb: - condition: service_started - - migrator: - restart: on-failure:1 - image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA - build: - context: . - dockerfile: Dockerfile - command: - - /bin/sh - - -c - - python manage.py migrate - env_file: - - $ENV celery: image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA @@ -85,8 +69,6 @@ services: - $ENV environment: - C_FORCE_ROOT=true - depends_on: - - celery-mdb celery_beat: image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA @@ -110,8 +92,6 @@ services: - node.role == worker env_file: - $ENV - depends_on: - - celery-mdb static-server: image: $CI_REGISTRY_IMAGE/static-server:$CI_COMMIT_SHA