@@ -17,6 +17,7 @@ from ml_model.services.midjourney import Midjourney from ml_model.services.mistral import Mistral from ml_model.services.musicgen import Musicgen from ml_model.services.pulid import Pulid +from ml_model.services.qwen import Qwen from ml_model.services.recraft import Recraft from ml_model.services.sdxlemoji import Sdxlemoji from ml_model.services.stablediffusion import Stablediffusion @@ -0,0 +1,113 @@ +import time +from datetime import timedelta +from decimal import Decimal +from typing import Iterator, Any + +from messages.models import Message +from ml_model.models import ModelCategory, ModelVersion, ModelInput, ModelParameter +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 Qwen(SimpleService): + """ + Qwen Service + contains abstract method make, which makes a generation + """ + + title = 'Qwen' + description = 'Нейросеть, способная генерировать еще больше текста из вашего текста' + category = ModelCategory(title='Чат-боты', slug='chat-bots') + versions = [ + ModelVersion(name='QwQ 32B', default=True, slug='qwq-32b'), + # ModelVersion(name='QwQ 32B', slug='qwq-32b:free'), + ] + inputs = [ModelInput(type=ModelInput.TypeChoices.TEXT, required=True), ] + parameters = [] + TOKENS_COST = { + 'qwq-32b': {'input': Decimal('24'), 'output': Decimal('36')}, # 1M tokens + 'qwq-32b:free': {'input': Decimal('0'), 'output': Decimal('0')}, # 1M tokens + } + + def calculate_price( + self, version: str, input_tokens: int, output_tokens: int + ) -> Decimal: + price_map = self.TOKENS_COST[version.split('/')[1]] + price = ( + input_tokens * price_map['input'] / 1_000_000 + + output_tokens * price_map['output'] / 1_000_000 + ) + return price.quantize(Decimal('0.1'), 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]: + start_time = time.time() + version = f'qwen/{input_message.info.pop('version', 'qwq-32b')}' + callback_data = { + 'provider': { + 'order': ['DeepInfra'] + }, + **input_message.info + } + messages = self.get_chat_history() + result = openrouter_run(version, messages, callback_data, self.title) + process_time = timedelta(seconds=(time.time() - start_time)) + self.handle_invoice( + input_message.content_object.model, + version=version, + 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): + 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')[:message_limit] + ) + ) + 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: + memory.pop(0) + character_length = sum(len(content['content']) for content in memory) + + return memory @@ -6,6 +6,7 @@ from io import BytesIO from typing import IO, Any, Dict import deepl +import httpx import replicate import requests from celery import shared_task @@ -112,6 +113,37 @@ def replicate_run(callback_url: str, payload: dict[str, Any]): ) +@shared_task +def openrouter_run(version: str, messages: list, callback_data: dict, model_name: str): + with httpx.Client( + base_url='https://openrouter.ai/api/v1', + headers={'Authorization': f'Bearer {settings.OPENROUTER_API_KEY}'}, + ) as client: + resp = client.post( + 'chat/completions', + json={ + 'model': version, + 'messages': messages, + **callback_data + }, + ) + if ( + (data := resp.json()) + and data.get('choices') + and ( + content := ','.join( + [choice['message']['content'] for choice in data.get('choices')] + ) + ) + ): + return ( + content, + data['usage']['prompt_tokens'], + data['usage']['completion_tokens'] + ) + raise Exception(f'No answer from {model_name}, please retry later') + + @shared_task def upscale_run(payload: dict[str, tuple[str, IO]]) -> list[str]: content = requests.post( @@ -3,6 +3,7 @@ SECRET_KEY=testtest DEBUG=true # NEURON MODELS +OPENROUTER_API_KEY=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 OPENAI_API_KEY=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 STABLE_DIFFUSION_API_KEY=sk-fztQxZobaL0SD7PgpmK7XMQlyNivpKFZNJnqAVG2CcbvAP6Z REPLICATE_API_KEY=r8_HBk6Ts5UJU60nDOUl1V6Uej4ihAAxUc3HAZLO