@@ -1,11 +1,16 @@ +import re import time from datetime import timedelta from decimal import Decimal -from typing import Any, Dict, Iterator +from typing import Any, Iterator + +import httpx +from django.conf import settings from messages.models import Message from ml_model.services.base import SimpleService from ml_model.tasks import openrouter_run +from poller.models import Proxy from tools.chats.models import Chat from tools.copywrite.models import Copywrite from tools.public_api.models import APIStore @@ -18,7 +23,22 @@ class Perplexity(SimpleService): """ TOKENS_COST = { - 'sonar': {'input': Decimal('300'), 'output': Decimal('300')}, # 1M tokens + 'sonar': { + 'input': Decimal('300'), + 'output': Decimal('300') + }, # 1M tokens + 'sonar-deep-research': { + 'input': Decimal('600'), # 1M tokens + 'output': Decimal('2400'), # 1M tokens + 'citation': Decimal('600'), # 1M tokens + 'search': Decimal('1500'), # 1K queries + 'reasoning': Decimal('900') # 1M tokens + }, + 'sonar-pro-search': { + 'input': Decimal('900'), # 1M tokens + 'output': Decimal('4500'), # 1M tokens + 'search': Decimal('5400'), # 1K queries + } } def calculate_price(self, version: str, input_tokens: int, output_tokens: int) -> Decimal: @@ -26,6 +46,8 @@ class Perplexity(SimpleService): price = ( input_tokens * price_map['input'] / 1_000_000 + output_tokens * price_map['output'] / 1_000_000 ) + if version == 'perplexity/sonar-pro-search': + price += price_map['search'] / 1_000 return price.quantize(Decimal('0.1'), rounding='ROUND_UP') def save_results(self, content: Iterator[Any], t: timedelta, save: bool = True) -> list[Message]: @@ -46,7 +68,38 @@ class Perplexity(SimpleService): callback_data = {'provider': {'order': ['Perplexity']}, **input_message.info} messages = self.get_chat_history() messages.append({'role': 'user', 'content': input_message.content}) - result = openrouter_run(version, messages, callback_data, 'Perplexity') + if version == 'perplexity/sonar-pro-search': + try: + for proxy in Proxy.objects.all(): + response = httpx.post( + url='https://openrouter.ai/api/v1/chat/completions', headers={'Authorization': f'Bearer {settings.OPENROUTER_API_KEY}'}, + proxy=f'{proxy.protocol}://{proxy.address}', timeout=600, + json={'model': version, 'messages': messages, **callback_data} + ) + if response.status_code not in (200, 201): + raise + response = response.json() + annotations = response['choices'][0]['message'].get('annotations', []) + content = response['choices'][0]['message']['content'] + if annotations: + content = ( + re.sub( + r'\[(\d+)\]', + lambda m: f'[{m.group(1)}]({str(annotations[int(m.group(1)) - 1]["url_citation"]["url"])})', + content + ) + + f'\n\nРесурсы:\n{"\n".join( + [ + f'{num}. {a["url_citation"]["title"]} ({a["url_citation"]["url"]})' + for num, a in enumerate(annotations, start=1) + ] + )}' + ) + result = [content, response['usage']['prompt_tokens'], response['usage']['completion_tokens']] + except Exception: + raise Exception(f'No answer from Perplexity, please retry later') + else: + result = openrouter_run(version, messages, callback_data, 'Perplexity') process_time = timedelta(seconds=(time.time() - start_time)) self.handle_invoice( input_message.content_object.model,