@@ -85,7 +85,7 @@ class OpenrouterAdapter: ) continue - if not input_tokens or not output_tokens: + if not input_tokens and not output_tokens: logger.error(f'Opernrouter failed get data about tokens for model {model_name}') input_tokens, output_tokens = cls._fallback_tokenize( model_name, messages, content + reasoning @@ -2,6 +2,7 @@ from ml_model.services.chatgpt import Chatgpt from ml_model.services.chatgpt_5 import Chatgpt_5 from ml_model.services.chatgpt_5_4 import Chatgpt_5_4 from ml_model.services.chatgpt_5_5 import Chatgpt_5_5 +from ml_model.services.chatgpt_5_6 import Chatgpt_5_6 from ml_model.services.claude import Claude from ml_model.services.codellama import Codellama from ml_model.services.dalle import Dalle @@ -22,6 +22,7 @@ from ml_model.exceptions import ( CorruptedFileError, FileExtensionNotSupported, InvalidParameterError, + ModelVersionNotAvailable, PaidPlanRequiredError, ) from ml_model.services import Chatgpt @@ -53,6 +54,8 @@ class Chatgpt_5_5(Chatgpt, StreamSimpleService, OpenAIStreamMixin): }, } + BASE_VERSION = 'gpt-5.5' + TOKEN_LIMITS = {'gpt-5.5': 1_050_000 // 2} FORMATION_INSTRUCTIONS = ( @@ -211,7 +214,7 @@ class Chatgpt_5_5(Chatgpt, StreamSimpleService, OpenAIStreamMixin): try: for proxy in Proxy.objects.all(): - json_data, predicted_input_tokens = self._build_payload(proxy, input_message, ctx) + json_data, predicted_input_tokens = self._build_payload(proxy, input_message, ctx, include_image_tool=False) model_name = ctx['model_name'] self.logger.info( f'Predicted input tokens (responses/input_tokens) для {model_name} - {predicted_input_tokens}' @@ -261,10 +264,14 @@ class Chatgpt_5_5(Chatgpt, StreamSimpleService, OpenAIStreamMixin): proxy: Proxy, input_message: Message, ctx: dict[str, Any], + *, + include_image_tool: bool = True ) -> tuple[dict[str, Any], int]: if not ctx: - model_name = 'gpt-5.5' info = input_message.info.copy() + model_name = info.get('version', self.BASE_VERSION) + if model_name is None or model_name not in self.TOKENS_COST: + raise ModelVersionNotAvailable(model_name, self.TOKENS_COST) user_system_prompt = info.pop('system_prompt', '') file = input_message.file image = None @@ -361,7 +368,7 @@ class Chatgpt_5_5(Chatgpt, StreamSimpleService, OpenAIStreamMixin): 'instructions': self.FORMATION_INSTRUCTIONS, 'tools': [], } - if ctx['has_full_access']: + if ctx['has_full_access'] and include_image_tool: json_data['tools'].append( { 'type': 'image_generation', @@ -435,7 +442,7 @@ class Chatgpt_5_5(Chatgpt, StreamSimpleService, OpenAIStreamMixin): + ctx['predict_embedding_tokens'] * self.TOOLS_TOKEN_COSTS[self.EMBEDDING_MODEL_FOR_BILLING]['output'] ) - if ctx['has_full_access']: + if ctx['has_full_access'] and include_image_tool: predicted_input_price += self.TOKENS_COST[model_name]['generated_image'] max_output_tokens = max( int( @@ -0,0 +1,62 @@ +from decimal import Decimal + +from ml_model.services import Chatgpt_5_5 + + +class Chatgpt_5_6(Chatgpt_5_5): + TOKENS_COST = { + 'gpt-5.6-sol': { + 'input': Decimal('0.0025'), # $5 / 1M tokens + 'output': Decimal('0.015'), # $30 / 1M tokens + 'web_search': { + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call + }, + 'code_interpreter': Decimal('15'), # 1 call + 'generated_image': Decimal('10.2'), + }, + 'gpt-5.6-luna': { + 'input': Decimal('0.0005'), # $1 / 1M tokens + 'output': Decimal('0.003'), # $6 / 1M tokens + 'web_search': { + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call + }, + 'code_interpreter': Decimal('15'), # 1 call + 'generated_image': Decimal('10.2'), + }, + 'gpt-5.6-terra': { + 'input': Decimal('0.00125'), # $2.5 / 1M tokens + 'output': Decimal('0.0075'), # $15 / 1M tokens + 'web_search': { + 'low': Decimal('5'), # 1 call + 'medium': Decimal('5'), # 1 call + 'high': Decimal('5'), # 1 call + }, + 'code_interpreter': Decimal('15'), # 1 call + 'generated_image': Decimal('10.2'), + }, + } + + BASE_VERSION = 'gpt-5.6-luna' + + BASE_SYSTEM = ( + 'You are an analytical assistant optimized for GPT-5.6.\n' + 'Provide accurate, evidence-based, and practical answers.\n\n' + 'Guidelines:\n' + '- Never fabricate facts or technical behavior\n' + '- Distinguish facts from assumptions and uncertainty\n' + '- Use available retrieved evidence when relevant\n' + '- Prefer official documentation and primary sources\n' + '- Lead with the conclusion, then include supporting evidence and material caveats\n' + '- Mention limitations only when they affect the answer\n' + '- Avoid unnecessary repetition or speculative discussion' + ) + + TOKEN_LIMITS = { + 'gpt-5.6-sol': 1_050_000 // 2, + 'gpt-5.6-luna': 1_050_000 // 2, + 'gpt-5.6-terra': 1_050_000 // 2, + }