@@ -8,6 +8,7 @@ from ml_model.services.djourney import Djourney from ml_model.services.epicphotogasm import Epicphotogasm from ml_model.services.flux import Flux from ml_model.services.fluxproultra import Fluxproultra +from ml_model.services.gemini import Gemini from ml_model.services.granite import Granite from ml_model.services.grok import Grok from ml_model.services.iconic import Iconic @@ -0,0 +1,136 @@ +import base64 +import time +import filetype + +from datetime import timedelta +from decimal import Decimal +from io import BytesIO +from PIL import Image + +from django.db.models.fields.files import FieldFile + +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 Gemini(SimpleService): + """ + Gemini Service + contains abstract method make, which makes a generation + """ + + TOKENS_COST = { + 'gemini-2.0-flash-001': { + 'input': Decimal('20'), + 'output': Decimal('80'), + 'input_imgs': Decimal('5.16'), + }, + } + + def calculate_price(self, version: str, input_tokens: int, output_tokens: int, image: FieldFile) -> 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 + ) + if image: + price += price_map['input_imgs'] / 1_000 + return price.quantize(Decimal('0.1'), 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]: + info = input_message.info.copy() + version = info.pop('version') + callback_data = { + 'provider': {'order': ['Google AI Studio']}, + **input_message.info, + } + messages = self.get_chat_history() + image = input_message.file + if image: + kind = filetype.guess(image.read(20)) + mime = kind.mime if kind else 'application/octet-stream' + normalized_image = Image.open(image) + format = 'jpeg' if kind.extension == 'jpg' else kind.extension + buf = BytesIO() + normalized_image.save(buf, format=format) + image_url = ( + f'data:{mime};base64,{base64.b64encode(buf.getvalue()).decode("utf-8")}' + ) + buf.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(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], + image=image + ) + 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: + character_length -= len(memory.pop(0)) + return memory @@ -3,7 +3,6 @@ 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 @@ -14,6 +13,7 @@ MISTRAL_API_KEY=CYtZSCQXZFzHcpJvWOjWNx4EHjf5kWQc DEEPL_API_KEY=4bb58b98-ca95-5978-9be0-ed437df6c15c:fx SERPER_API_KEY=ed8e0dbcc26dacf3f7f99fbc8b3add9ada0c793e FLUX_API_KEY=dccaf377-aecf-4cf0-aff4-dde47cee340d +OPENROUTER_API_KEY=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 # EXTERNAL SERVICES OPENAI_PROXY_HOST=neuron-proxy:8080