@@ -96,7 +96,7 @@ class Chatgpt(SimpleService): @property def neuron_model(self): - return NeuronModel.objects.get(title='ChatGPT') + return NeuronModel.objects.get(title='ChatGPT 4') def make( self, @@ -153,7 +153,9 @@ class Chatgpt(SimpleService): get_session_history=lambda _: chat_history, ) 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) + input_tokens, input_embedding_tokens = self._get_input_tokens( + file, image, chunks, chat_history, llm_input, model_name + ) output_tokens = 0 self.assert_enough_balance( input_tokens, image_size, model=self.llm.model_name, embedding_tokens=input_embedding_tokens @@ -427,7 +429,8 @@ class Chatgpt(SimpleService): input_tokens: int, image_size: tuple | None, model: str = 'gpt-3.5-turbo', - embedding_tokens: int = 0 + embedding_tokens: int = 0, + output_tokens: int = 0, ): balance = PaymentPlanSelector(self.store.user).get_current_balance() total_tokens = input_tokens @@ -439,8 +442,9 @@ class Chatgpt(SimpleService): embedding_tokens * self.TOOLS_TOKEN_COSTS[self.EMBEDDING_MODEL_FOR_BILLING]['output'] ) - if input_cost > balance: - raise InsufficientBalance(balance, input_cost) + output_cost = self.TOKENS_COST[model]['output'] * output_tokens + if input_cost + output_cost > balance: + raise InsufficientBalance(balance, input_cost + output_cost) def calculate_price( self, @@ -520,7 +524,7 @@ class Chatgpt(SimpleService): image_data = {'type': 'image_url', 'image_url': {'url': image_url}} return normalized_image, image_size, image_data - def _get_input_tokens(self, file, image, chunks, chat_history, llm_input): + def _get_input_tokens(self, file, image, chunks, chat_history, llm_input, model_name=None): input_embedding_tokens = 0 if file and not image: if sum([len(chunk.content) for chunk in chunks]) > 20_000: @@ -528,7 +532,7 @@ class Chatgpt(SimpleService): input_embedding_tokens = len(chunks) * 600 else: input_tokens = self.count_text_tokens([*chat_history.messages, *llm_input, *chunks]) - elif image: + elif image and model_name in ('gpt-4o', 'gpt-4o-mini'): input_tokens = self.count_text_tokens(llm_input) else: input_tokens = self.count_text_tokens([*chat_history.messages, *llm_input]) @@ -603,7 +607,12 @@ class Chatgpt(SimpleService): endpoint == 'responses' and (data := resp.json()) and data.get('output') - and (image := data['output'][0]['result']) + and ( + image := next( + (item['result'] for item in data['output'] if item.get('result')), + None, + ) + ) ): input_tokens = resp.json()['usage']['input_tokens'] output_tokens = resp.json()['usage']['output_tokens'] @@ -127,7 +127,7 @@ class Chatgpt_5(Chatgpt): 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 + file, image, chunks, chat_history, llm_input, model_name ) self.assert_enough_balance( input_tokens, image_size, model=model_name, embedding_tokens=input_embedding_tokens @@ -106,6 +106,12 @@ class Chatgpt_5_4(Chatgpt): info = input_message.info.copy() model_name = info.pop('version', 'gpt-5.4') user_system_prompt = info.pop('system_prompt', '') + plan_info = self.store.user.payment_plan + is_free_plan = plan_info and plan_info.plan.price <= 0 + if is_free_plan: + info.pop('web_search', None) + info.pop('code_interpreter', None) + info.pop('verbosity', None) input_content = [{'type': 'text', 'text': input_message.content or ''}] file = input_message.file image = None @@ -134,10 +140,14 @@ class Chatgpt_5_4(Chatgpt): 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 + file, image, chunks, chat_history, llm_input, model_name ) self.assert_enough_balance( - input_tokens, image_size, model=model_name, embedding_tokens=input_embedding_tokens + input_tokens, + image_size, + model=model_name, + embedding_tokens=input_embedding_tokens, + output_tokens=500 if is_free_plan else 4000, ) for proxy in Proxy.objects.all(): system = chat_history.messages.pop(0) @@ -175,17 +185,20 @@ class Chatgpt_5_4(Chatgpt): 'Используй системный промпт. Содержание файла: ' f'{"".join(text_chunks)}. Вопрос: {input_message.content}' ) - json_data = { - 'model': model_name, - 'input': messages, - 'tools': [ + tools = [] + if not is_free_plan: + tools.append( { 'type': 'image_generation', 'size': '1024x1024', 'quality': 'medium', 'model': 'gpt-image-1.5', } - ], + ) + json_data = { + 'model': model_name, + 'input': messages, + 'tools': tools, 'instructions': ( 'Форматирование — обязательное требование. Выполняй строго по правилам:\n\n' "1) Используй реальные символы новой строки, не выводи '\\n' как текст — вставляй переносы.\n\n" @@ -205,7 +218,10 @@ class Chatgpt_5_4(Chatgpt): 'добавляй две пустые строки между абзацами и блоками для улучшения читаемости.' ), } - if reasoning := info.get('reasoning'): + if is_free_plan: + json_data['max_output_tokens'] = 500 + json_data['reasoning'] = {'effort': 'none', 'summary': 'auto'} + elif reasoning := info.get('reasoning'): reasoning_data = { 'Минимальный': 'minimal', 'Низкий': 'low',