@@ -1,4 +1,5 @@ from ml_model.services.chatgpt import Chatgpt +from ml_model.services.chatgpt_5 import Chatgpt_5 from ml_model.services.claude import Claude from ml_model.services.codellama import Codellama from ml_model.services.dalle import Dalle @@ -69,10 +69,6 @@ class Chatgpt(SimpleService): 'input': Decimal('0.0022'), 'output': Decimal('0.0022'), }, - 'o1-preview': { - 'input': Decimal('0.03'), - 'output': Decimal('0.03'), - }, 'gpt-4o-mini': { 'input': Decimal('0.0003'), 'output': Decimal('0.0003'), @@ -91,35 +87,6 @@ class Chatgpt(SimpleService): 'high': Decimal('25') # 1 call } }, - 'gpt-4.5-preview': { - 'input': Decimal('0.075'), - 'output': Decimal('0.075'), - }, - 'gpt-5': { - 'input': Decimal('0.000625'), - 'output': Decimal('0.005'), - 'web_search': { - 'low': Decimal('5'), # 1 call - 'medium': Decimal('5'), # 1 call - 'high': Decimal('5') # 1 call - }, - 'code_interpreter': Decimal('15') - }, - 'gpt-5-mini': { - 'input': Decimal('0.000125'), - 'output': Decimal('0.001'), - 'web_search': { - 'low': Decimal('5'), # 1 call - 'medium': Decimal('5'), # 1 call - 'high': Decimal('5') # 1 call - }, - 'code_interpreter': Decimal('15') - }, - 'gpt-5-nano': { - 'input': Decimal('0.000025'), - 'output': Decimal('0.0002'), - 'code_interpreter': Decimal('15') - }, 'gpt-oss-120b': { 'input': Decimal('0.0002'), 'output': Decimal('0.0002') @@ -132,6 +99,14 @@ class Chatgpt(SimpleService): } } + TOKEN_LIMITS = { + 'o3-mini': 100_000, + 'gpt-4o-mini': 64_000, + 'gpt-4o': 64_000, + 'gpt-4.5-preview': 64_000, + 'gpt-oss-120b': 65_500, + } + def __init__(self, store: BaseStore) -> None: super().__init__(store) self.logger = logging.getLogger(self.__class__.__name__) @@ -155,46 +130,19 @@ class Chatgpt(SimpleService): image_size = None normalized_image = None embedding_tokens = 0 + chunks = [] if file: try: file_bytes = input_message.file.read() kind = filetype.guess(file_bytes[:20]) - file_extension = kind.extension - if file_extension == 'zip': - file_extension = None - signatures = { - 'xlsx': 'xl/workbook.xml', - 'docx': 'word/document.xml' - } - with zipfile.ZipFile(BytesIO(file_bytes), 'r') as zip_file: - namelist = zip_file.namelist() - for format_name, required_file in signatures.items(): - if required_file in namelist: - file_extension = format_name - break - if not file_extension: - raise - if file_extension == 'pdf': - raw_text = self.get_pdf_data(file_bytes) - text = re.sub(r'\n{2,}', '\n', raw_text) - chunks = self.split_text_to_chunks(text) - elif file_extension in ('doc', 'docx'): - raw_text = self.get_word_data(file_extension, file_bytes) - text = re.sub(r'\n{2,}', '\n', raw_text) - chunks = self.split_text_to_chunks(text) - elif file_extension == 'xlsx': - chunks = self.split_text_to_chunks(self.get_xlsx_data(file_bytes)) + raw_file_extension = kind.extension + file_extension = self._get_file_extension(raw_file_extension, file_bytes) + if file_extension in ('pdf', 'doc', 'docx', 'xlsx'): + chunks = self._get_file_data(file_extension, file_bytes) else: image = file mime = kind.mime if kind else 'application/octet-stream' - normalized_image = Image.open(BytesIO(file_bytes)) - format = 'jpeg' if file_extension == 'jpg' else file_extension - buf = BytesIO() - normalized_image.save(buf, format=format) - image_url = f'data:{mime};base64,{base64.b64encode(buf.getvalue()).decode("utf-8")}' - buf.close() - image_size = normalized_image.size - image_data = {'type': 'image_url', 'image_url': {'url': image_url}} + normalized_image, image_size, image_data = self._get_image_data(mime, file_bytes, file_extension) input_content.append(image_data) except: raise FileExtensionNotSupported(['PDF', 'DOC', 'DOCX', 'XLSX', 'JPG', 'JPEG', 'PNG']) @@ -203,132 +151,27 @@ class Chatgpt(SimpleService): model=model_name, http_client=httpx.Client(proxy=f'{proxy.protocol}://{proxy.address}'), ) - 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('web_search'): - del info['web_search'] - 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), - } + 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), + } self.llm.tiktoken_model_name = 'gpt-4' if model_name not in self.TOKENS_COST.keys(): raise Exception(_('No matching version found')) chat_history = self.get_chat_history(model_name=model_name) chat_history.add_message(HumanMessage(content=input_message.content)) - if model_name in ('o1-preview', 'o1-mini'): - chat_history.messages.pop(0) conversation = RunnableWithMessageHistory( runnable=self.llm, get_session_history=lambda _: chat_history, ) llm_input = [SystemMessage(content=user_system_prompt), HumanMessage(content=input_content)] - input_embedding_tokens = 0 - if file and not image: - if sum([len(chunk.content) for chunk in chunks]) > 20_000: - input_tokens = self.count_text_tokens([*chat_history.messages, *llm_input, *chunks[:10]]) - input_embedding_tokens = len(chunks) * 600 - else: - input_tokens = self.count_text_tokens([*chat_history.messages, *llm_input, *chunks]) - elif image: - input_tokens = self.count_text_tokens(llm_input) - else: - input_tokens = self.count_text_tokens([*chat_history.messages, *llm_input]) + input_tokens, input_embedding_tokens = self._get_input_tokens(file, image, chunks, chat_history, llm_input) output_tokens = 0 self.assert_enough_balance( input_tokens, image_size, model=self.llm.model_name, embedding_tokens=input_embedding_tokens ) - if model_name in ('gpt-5', 'gpt-5-mini', 'gpt-5-nano'): - system = chat_history.messages.pop(0) - messages = [ - {'role': 'user' if isinstance(msg, HumanMessage) else 'assistant', 'content': msg.content} - for msg in chat_history.messages - ] - messages.insert(0, {'role': 'system', 'content': system.content}) - messages.insert(0, {'role': 'system', 'content': user_system_prompt}) - if image: - messages[-1]['content'] = [ - {'type': 'input_text', 'text': input_message.content}, - {'type': 'input_image', 'image_url': image_data['image_url']['url']} - ] - elif file: - if sum([len(chunk.content) for chunk in chunks]) > 20_000: - document_name = chunks[0].content.partition(f':{chr(10)}')[2].split(f'{chr(10)}')[0][:100] - embedding_tokens, file_data = self.get_large_file_data(chunks, proxy, input_message.content) - messages[-1]['content'] = self.make_embeddings_prompt( - document_name=document_name, section_texts=file_data, question=input_message.content - ) - else: - messages[-1]['content'] = (f'Используй системный промпт. Содержание файла: ' - f'{chunks}. Вопрос: {input_message.content}') - json_data = { - 'model': model_name, - 'input': messages, - 'tools': [], - 'instructions': ( - "Форматирование — обязательное требование. Выполняй строго по правилам:\n\n" - "1) Используй реальные символы новой строки, не выводи '\\n' как текст — вставляй переносы.\n\n" - "2) Абзацы: между абзацами ставь две пустые строки (два символа новой строки подряд).\n\n" - "3) Нумерованные и маркированные списки: каждый пункт на отдельной строке;\n" - " между списком и текстом оставляй две пустые строки.\n\n" - "4) Блоки кода: любые фрагменты кода выделяй тройными бэктиками (```) с указанием языка программирования;\n" - " перед и после блока оставляй две пустые строки.\n\n" - "5) Заголовки абзацев: делай крупным, используя Markdown '####' (например, '### Заголовок');\n" - " выделяй жирным (**Заголовок**); оставляй две пустые строки перед и после заголовка.\n\n" - "6) Используй Markdown для всего форматирования, не используй HTML.\n\n" - "7) Исправление формата: если формат неверный, перепиши ответ и верни исправленный вариант.\n\n" - "Строго разделяй текст на абзацы с жирными заголовками;\n" - "нумерованные и маркированные списки выводи с переносами строк;\n" - "блоки кода — с тройными бэктиками и указанием языка;\n" - "не выводи '\\n' как текст, используйте реальные переносы строк;\n" - "добавляй две пустые строки между абзацами и блоками для улучшения читаемости." - ) - } - if info.get('reasoning'): - json_data['reasoning'] = {} - reasoning_data = { - 'Минимальный': 'minimal', - 'Низкий': 'low', - 'Средний': 'medium', - 'Высокий': 'high' - } - json_data['reasoning']['effort'] = reasoning_data[info['reasoning']] - json_data['reasoning']['summary'] = 'auto' - if info.get('reasoning') == 'Минимальный': - info.pop('web_search', None) - info.pop('code_interpreter', None) - if model_name == 'gpt-5-nano': - info.pop('web_search', None) - if info.get('web_search', 'Отключено') != 'Отключено': - search_context_size, json_data = self.get_web_search_data( - info.get('web_search', 'Средний контекст'), - model_name, - messages - ) - info['web_search'] = search_context_size - if info.get('code_interpreter') is True: - json_data['tools'].append( - { - 'type': 'code_interpreter', - 'container': {'type': 'auto'} - } - ) - messages[-1]['content'] += 'the python tool' - input_tokens, output_tokens, response = self.call_openai_api( - proxy=proxy, - endpoint='responses', - json_data=json_data - ) - elif model_name == 'gpt-oss-120b': + if model_name == 'gpt-oss-120b': system = chat_history.messages.pop(0) messages = [ {'role': 'user' if isinstance(msg, HumanMessage) else 'assistant', 'content': msg.content} @@ -367,7 +210,7 @@ class Chatgpt(SimpleService): response = AIMessage(content=content.replace('\\n', '\n')) else: raise Exception('GPT not answer correctly, please retry later') - elif model_name in ('o3-mini', 'gpt-4.5-preview'): + elif model_name == 'o3-mini': system = chat_history.messages.pop(0) messages = [ {'role': 'user' if isinstance(msg, HumanMessage) else 'assistant', 'content': msg.content} @@ -396,15 +239,13 @@ class Chatgpt(SimpleService): } input_tokens, output_tokens, response = self.call_openai_api(proxy=proxy, endpoint='chat/completions',json_data=json_data) elif info.get('web_search', 'Отключено') != 'Отключено': - if model_name not in ('o1-preview', 'o1-mini'): - system = chat_history.messages.pop(0) + system = chat_history.messages.pop(0) messages = [ {'role': 'user' if isinstance(msg, HumanMessage) else 'assistant', 'content': msg.content} for msg in chat_history.messages ] - if model_name not in ('o1-preview', 'o1-mini'): - messages.insert(0, {'role': 'system', 'content': system.content}) - messages.insert(0, {'role': 'system', 'content': user_system_prompt}) + messages.insert(0, {'role': 'system', 'content': system.content}) + messages.insert(0, {'role': 'system', 'content': user_system_prompt}) search_context_size, json_data = self.get_web_search_data( info.get('web_search', 'Средний контекст'), model_name, @@ -460,9 +301,6 @@ class Chatgpt(SimpleService): config={'configurable': {'session_id': 'default'}}, ) else: - # Somehow this chain doesn't support Vision, even though ChatOpenAI (above) does. - if model_name in ('o1-preview', 'o1-mini'): - llm_input.pop(0) response = conversation.invoke( {'input': llm_input}, config={'configurable': {'session_id': 'default'}}, @@ -475,7 +313,7 @@ class Chatgpt(SimpleService): if ( image and normalized_image - and model_name not in ('o3-mini', 'gpt-4.5-preview', 'gpt-5', 'gpt-5-mini', 'gpt-5-nano') + and model_name != 'o3-mini' ): self.logger.info(f'Input количество токенов БЕЗ картинки {model_name} - {input_tokens}') input_tokens += self.count_image_tokens(normalized_image.size, model_name) @@ -513,17 +351,7 @@ class Chatgpt(SimpleService): is_sent=True, ).order_by('-created_at')[1:] - token_limits = { - 'o3-mini': 100_000, - 'o1-preview': 100_000, - 'gpt-4o-mini': 64_000, - 'gpt-4o': 64_000, - 'gpt-4.5-preview': 64_000, - 'gpt-5': 200_000, - 'gpt-5-mini': 200_000, - 'gpt-5-nano': 200_000, - 'gpt-oss-120b': 65_500, - } + token_limits = self.TOKEN_LIMITS tokens = 0 history: List[BaseMessage] = [] for message in air_messages.iterator(5): @@ -537,20 +365,19 @@ class Chatgpt(SimpleService): tokens += self.count_text_tokens(air_message) history.append(air_message[0]) memory = InMemoryChatMessageHistory() - if model_name not in ('o1-preview', 'o1-mini'): - memory.add_message(SystemMessage( + memory.add_message(SystemMessage( + content=( + 'Think step by step. Use full context. Prioritize depth, clarity, and justification. ' + 'Be thorough and expansive.' + ) + )) + memory.add_message(SystemMessage( content=( - 'Think step by step. Use full context. Prioritize depth, clarity, and justification. ' - 'Be thorough and expansive.' + 'Отныне все ответы должны быть представлены как единая строка (str). Не использовать никаких ' + 'структурированных форматов, таких как JSON, словари (dict) или списки (list). ' + 'Любая информация должна быть преобразована в простой строковый текст (str).' ) )) - memory.add_message(SystemMessage( - content=( - 'Отныне все ответы должны быть представлены как единая строка (str). Не использовать никаких ' - 'структурированных форматов, таких как JSON, словари (dict) или списки (list). ' - 'Любая информация должна быть преобразована в простой строковый текст (str).' - ) - )) memory.add_messages(list(reversed(history))) return memory @@ -635,6 +462,54 @@ class Chatgpt(SimpleService): return total_tokens + def _get_file_extension(self, raw_file_extension: str, file_bytes: bytes) -> str: + if raw_file_extension == 'zip': + signatures = { + 'xlsx': 'xl/workbook.xml', + 'docx': 'word/document.xml' + } + with zipfile.ZipFile(BytesIO(file_bytes), 'r') as zip_file: + namelist = zip_file.namelist() + for format_name, required_file in signatures.items(): + if required_file in namelist: + return format_name + raise + return raw_file_extension + + def _get_image_data(self, mime: str, file_bytes: bytes, file_extension: str) -> Tuple: + normalized_image = Image.open(BytesIO(file_bytes)) + format = 'jpeg' if file_extension == 'jpg' else file_extension + buf = BytesIO() + normalized_image.save(buf, format=format) + image_url = f'data:{mime};base64,{base64.b64encode(buf.getvalue()).decode("utf-8")}' + buf.close() + image_size = normalized_image.size + image_data = {'type': 'image_url', 'image_url': {'url': image_url}} + return normalized_image, image_size, image_data + + def _get_file_data(self, file_extension: str, file_bytes: bytes) -> list[HumanMessage]: + is_word = file_extension in ('doc', 'docx') + method_name = 'word' if is_word else file_extension + operation = getattr(self, f'get_{method_name}_data') + text = operation(file_extension, file_bytes) if is_word else operation(file_bytes) + if file_extension != 'xlsx': + text = re.sub(r'\n{2,}', '\n', text) + return self.split_text_to_chunks(text) + + def _get_input_tokens(self, file, image, chunks, chat_history, llm_input): + input_embedding_tokens = 0 + if file and not image: + if sum([len(chunk.content) for chunk in chunks]) > 20_000: + input_tokens = self.count_text_tokens([*chat_history.messages, *llm_input, *chunks[:10]]) + input_embedding_tokens = len(chunks) * 600 + else: + input_tokens = self.count_text_tokens([*chat_history.messages, *llm_input, *chunks]) + elif image: + input_tokens = self.count_text_tokens(llm_input) + else: + input_tokens = self.count_text_tokens([*chat_history.messages, *llm_input]) + return input_tokens, input_embedding_tokens + def get_large_file_data(self, chunks, proxy, user_content): redis_client = redis.Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=0) embedding_tokens = 0 @@ -0,0 +1,216 @@ +import time +from datetime import timedelta +from decimal import Decimal + +import filetype +from langchain_core.messages import HumanMessage, SystemMessage + +from messages.models import Message +from ml_model.exceptions import FileExtensionNotSupported +from ml_model.services import Chatgpt +from poller.models import Proxy + + +class Chatgpt_5(Chatgpt): + TOKENS_COST = { + 'gpt-5': { + 'input': Decimal('0.000625'), + 'output': Decimal('0.005'), + 'web_search': { + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5') # 1 call + }, + 'code_interpreter': Decimal('15') + }, + 'gpt-5-mini': { + 'input': Decimal('0.000125'), + 'output': Decimal('0.001'), + 'web_search': { + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5') # 1 call + }, + 'code_interpreter': Decimal('15') + }, + 'gpt-5-nano': { + 'input': Decimal('0.000025'), + 'output': Decimal('0.0002'), + 'code_interpreter': Decimal('15') + }, + 'gpt-5.1': { + 'input': Decimal('0.000625'), + 'output': Decimal('0.005'), + 'web_search': { + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5') # 1 call + }, + 'code_interpreter': Decimal('15') # 1 call + }, + 'gpt-5-pro': { + 'input': Decimal('0.0075'), + 'output': Decimal('0.06'), + 'web_search': { + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5') # 1 call + }, + }, + 'gpt-5.1-codex-max': { + 'input': Decimal('0.000625'), + 'output': Decimal('0.005'), + }, + 'gpt-5.1-codex': { + 'input': Decimal('0.000625'), + 'output': Decimal('0.005'), + }, + 'gpt-5-codex': { + 'input': Decimal('0.000625'), + 'output': Decimal('0.005'), + } + } + + TOKEN_LIMITS = { + 'gpt-5': 200_000, + 'gpt-5-mini': 200_000, + 'gpt-5-nano': 200_000, + 'gpt-5.1': 200_000, + 'gpt-5-pro': 200_000, + 'gpt-5.1-codex-max': 200_000, + 'gpt-5.1-codex': 200_000, + 'gpt-5-codex': 200_000, + } + + def make( + self, + input_message: Message, + save: bool = True, + ) -> list[Message]: + start_time = time.time() + info = input_message.info.copy() + model_name = info.pop('version', 'gpt-5') + user_system_prompt = info.pop('system_prompt', '') + input_content = [{'type': 'text', 'text': input_message.content or ''}] + file = input_message.file + image = None + image_size = None + embedding_tokens = 0 + chunks = [] + if file: + try: + file_bytes = input_message.file.read() + kind = filetype.guess(file_bytes[:20]) + raw_file_extension = kind.extension + file_extension = self._get_file_extension(raw_file_extension, file_bytes) + if file_extension in ('pdf', 'doc', 'docx', 'xlsx'): + chunks = self._get_file_data(file_extension, file_bytes) + else: + image = file + mime = kind.mime if kind else 'application/octet-stream' + _, image_size, image_data = self._get_image_data(mime, file_bytes, file_extension) + except Exception: + raise FileExtensionNotSupported(['PDF', 'DOC', 'DOCX', 'XLSX', 'JPG', 'JPEG', 'PNG']) + chat_history = self.get_chat_history(model_name=model_name) + chat_history.add_message(HumanMessage(content=input_message.content)) + llm_input = [SystemMessage(content=user_system_prompt), HumanMessage(content=input_content)] + input_tokens, input_embedding_tokens = self._get_input_tokens(file, image, chunks, chat_history, llm_input) + self.assert_enough_balance( + input_tokens, image_size, model=model_name, embedding_tokens=input_embedding_tokens + ) + for proxy in Proxy.objects.all(): + system = chat_history.messages.pop(0) + messages = [ + {'role': 'user' if isinstance(msg, HumanMessage) else 'assistant', 'content': msg.content} + for msg in chat_history.messages + ] + messages.insert(0, {'role': 'system', 'content': system.content}) + messages.insert(0, {'role': 'system', 'content': user_system_prompt}) + if image: + messages[-1]['content'] = [ + {'type': 'input_text', 'text': input_message.content}, + {'type': 'input_image', 'image_url': image_data['image_url']['url']} + ] + elif file: + if sum([len(chunk.content) for chunk in chunks]) > 20_000: + document_name = chunks[0].content.partition(f':{chr(10)}')[2].split(f'{chr(10)}')[0][:100] + embedding_tokens, file_data = self.get_large_file_data(chunks, proxy, input_message.content) + messages[-1]['content'] = self.make_embeddings_prompt( + document_name=document_name, section_texts=file_data, question=input_message.content + ) + else: + messages[-1]['content'] = (f'Используй системный промпт. Содержание файла: ' + f'{chunks}. Вопрос: {input_message.content}') + json_data = { + 'model': model_name, + 'input': messages, + 'tools': [], + 'instructions': ( + "Форматирование — обязательное требование. Выполняй строго по правилам:\n\n" + "1) Используй реальные символы новой строки, не выводи '\\n' как текст — вставляй переносы.\n\n" + "2) Абзацы: между абзацами ставь две пустые строки (два символа новой строки подряд).\n\n" + "3) Нумерованные и маркированные списки: каждый пункт на отдельной строке;\n" + " между списком и текстом оставляй две пустые строки.\n\n" + "4) Блоки кода: любые фрагменты кода выделяй тройными бэктиками (```) с указанием языка программирования;\n" + " перед и после блока оставляй две пустые строки.\n\n" + "5) Заголовки абзацев: делай крупным, используя Markdown '####' (например, '### Заголовок');\n" + " выделяй жирным (**Заголовок**); оставляй две пустые строки перед и после заголовка.\n\n" + "6) Используй Markdown для всего форматирования, не используй HTML.\n\n" + "7) Исправление формата: если формат неверный, перепиши ответ и верни исправленный вариант.\n\n" + "Строго разделяй текст на абзацы с жирными заголовками;\n" + "нумерованные и маркированные списки выводи с переносами строк;\n" + "блоки кода — с тройными бэктиками и указанием языка;\n" + "не выводи '\\n' как текст, используйте реальные переносы строк;\n" + "добавляй две пустые строки между абзацами и блоками для улучшения читаемости." + ) + } + if info.get('reasoning'): + json_data['reasoning'] = {} + reasoning_data = { + 'Минимальный': 'minimal', + 'Низкий': 'low', + 'Средний': 'medium', + 'Высокий': 'high' + } + json_data['reasoning']['effort'] = reasoning_data[info['reasoning']] + json_data['reasoning']['summary'] = 'auto' + if info.get('reasoning') == 'Минимальный': + info.pop('web_search', None) + info.pop('code_interpreter', None) + if model_name in ('gpt-5-nano', 'gpt-5-codex', 'gpt-5.1-codex', 'gpt-5.1-codex-max'): + info.pop('web_search', None) + if info.get('web_search', 'Отключено') != 'Отключено': + search_context_size, json_data = self.get_web_search_data( + info.get('web_search', 'Средний контекст'), + model_name, + messages + ) + info['web_search'] = search_context_size + if info.get('code_interpreter') is True and model_name in ('gpt-5-mini', 'gpt-5-nano', 'gpt-5', 'gpt-5.1'): + json_data['tools'].append( + { + 'type': 'code_interpreter', + 'container': {'type': 'auto'} + } + ) + messages[-1]['content'] += 'the python tool' + input_tokens, output_tokens, response = self.call_openai_api( + proxy=proxy, + endpoint='responses', + json_data=json_data + ) + self.logger.info(f'Input количество токенов для {model_name} - {input_tokens}') + self.logger.info(f'Output количество токенов для {model_name} - {output_tokens}') + self.logger.info(f'Embedding количество токенов для {model_name} - {embedding_tokens}') + self.logger.info(f'Общее количество токенов для {model_name} - {input_tokens + output_tokens + embedding_tokens}') + process_time = timedelta(seconds=time.time() - start_time) + self.handle_invoice( + self.neuron_model, + input_tokens, + output_tokens, + model_name, + info, + embedding_tokens + ) + msgs = self.save_results([response], process_time, save) + return msgs @@ -72,7 +72,7 @@ class Raifgpt(Chatgpt): if file_extension == '.pdf': raw_text = self.get_pdf_data(file) elif file_extension in ('.doc', '.docx'): - raw_text = self.get_word_data(file_extension, file) + raw_text = self.get_word_data(file_extension[1:], file.read()) else: raise FileExtensionNotSupported(['PDF', 'DOC', 'DOCX']) text = re.sub(r'\n{2,}', '\n', raw_text)