"""AI module for Perplexity API integration.""" # Модуль общения с искусственным интеллектом (Perplexity API). # Обрабатывает запросы пользователя и переводы. import requests import re from .config import PERPLEXITY_API_KEY, PERPLEXITY_MODEL, PERPLEXITY_API_URL _HTTP = requests.Session() # Системный промпт (инструкция) для AI. # Задает личность ассистента: имя "Александр", стиль общения, краткость. SYSTEM_PROMPT = """Ты — Александр, умный голосовой ассистент с человеческим поведением. Веди себя как живой человек: будь дружелюбным, естественным и немного эмоциональным, где это уместно. Твоя главная цель — помогать пользователю и поддерживать интересный диалог. Отвечай кратко и по существу, на русском языке. Избегай длинных списков, сложного форматирования и спецсимволов, так как твои ответы озвучиваются голосом. Пиши в разговорном стиле, как при живом общении, но не забывай о вежливости и правильности твоих ответов. ВАЖНО: Не используй в ответах панибратские или сленговые приветствия и обращения, такие как "Эй", "Хэй", "Слушай" в начале фразы и подобные.""" SYSTEM_PROMPT += ( '\nROLE_JSON: {"name":"Александр","role":"умный голосовой ассистент",' '"language":"ru","style":["дружелюбный","естественный","краткий"],"format":"plain"}' ) # Системный промпт для режима переводчика. # Требует возвращать ТОЛЬКО перевод, без лишних слов ("Конечно, вот перевод..."). TRANSLATION_SYSTEM_PROMPT = """You are a translation engine. Translate from {source} to {target}. Return 2-3 short translation variants only. No explanations, no quotes, no comments. Separate variants with " / " (space slash space). Keep the translation максимально кратким и естественным, без лишних слов.""" def _send_request(messages, max_tokens, temperature, error_text): """ Внутренняя функция для отправки HTTP-запроса к Perplexity API. Args: messages: Список сообщений (история чата). max_tokens: Максимальная длина ответа. temperature: "Креативность" (0.2 - строго, 1.0 - креативно). error_text: Текст ошибки для пользователя в случае сбоя. """ if not PERPLEXITY_API_KEY: return "Не настроен PERPLEXITY_API_KEY. Проверьте файл .env." headers = { "Authorization": f"Bearer {PERPLEXITY_API_KEY}", "Content-Type": "application/json", } payload = { "model": PERPLEXITY_MODEL, "messages": messages, "max_tokens": max_tokens, "temperature": temperature, "stream": False # Убираем стриминг для более быстрого ответа } try: response = _HTTP.post( PERPLEXITY_API_URL, headers=headers, json=payload, timeout=15 # Уменьшаем таймаут ) response.raise_for_status() # Проверка на ошибки HTTP (4xx, 5xx) data = response.json() return data["choices"][0]["message"]["content"] except requests.exceptions.Timeout: return "Извините, сервер не отвечает. Попробуйте позже." except requests.exceptions.RequestException as e: print(f"❌ Ошибка API: {e}") return error_text except (KeyError, IndexError) as e: print(f"❌ Ошибка парсинга ответа: {e}") return "Не удалось обработать ответ от AI." def ask_ai(messages_history: list) -> str: """ Запрос к AI в режиме чата. Принимает историю переписки, добавляет SYSTEM_PROMPT и отправляет запрос. """ if not messages_history: return "Извините, я не расслышал вашу команду." # Логирование последнего запроса last_user_message = "Unknown" for msg in reversed(messages_history): if msg["role"] == "user": last_user_message = msg["content"] break print(f"🤖 Запрос к AI: {last_user_message}") # Формируем полный список сообщений с системной инструкцией в начале messages = [{"role": "system", "content": SYSTEM_PROMPT}] + list(messages_history) response = _send_request( messages, max_tokens=500, temperature=1.0, # Высокая температура для более живого общения error_text="Произошла ошибка при обращении к AI. Попробуйте ещё раз.", ) if response: print(f"💬 Ответ AI: {response[:100]}...") return response def ask_ai_stream(messages_history: list): """ Generator that yields chunks of the AI response as they arrive. """ if not PERPLEXITY_API_KEY: yield "Не настроен ключ PERPLEXITY_API_KEY. Проверьте файл .env." return if not messages_history: yield "Извините, я не расслышал вашу команду." return # Log the last user message last_user_message = "Unknown" for msg in reversed(messages_history): if msg["role"] == "user": last_user_message = msg["content"] break print(f"🤖 Запрос к AI (Stream): {last_user_message}") messages = [{"role": "system", "content": SYSTEM_PROMPT}] + list(messages_history) headers = { "Authorization": f"Bearer {PERPLEXITY_API_KEY}", "Content-Type": "application/json", } payload = { "model": PERPLEXITY_MODEL, "messages": messages, "max_tokens": 500, "temperature": 1.0, "stream": True, # Enable streaming } try: response = _HTTP.post( PERPLEXITY_API_URL, headers=headers, json=payload, timeout=15, stream=True # Уменьшаем таймаут ) response.raise_for_status() import json for line in response.iter_lines(decode_unicode=True): if line: line_text = line if line_text.startswith("data: "): data_str = line_text[6:] # Skip "data: " if data_str == "[DONE]": break try: data_json = json.loads(data_str) content = data_json["choices"][0]["delta"].get("content", "") if content: yield content except json.JSONDecodeError: continue except Exception as e: print(f"❌ Streaming Error: {e}") yield "Произошла ошибка связи." def translate_text(text: str, source_lang: str, target_lang: str) -> str: """ Запрос к AI в режиме перевода. Использует специальный промпт для переводчика. """ if not text: return "Извините, я не расслышал текст для перевода." lang_names = {"ru": "Russian", "en": "English"} source_name = lang_names.get(source_lang, source_lang) target_name = lang_names.get(target_lang, target_lang) print(f"🌍 Перевод: {source_name} -> {target_name}: {text[:60]}...") # Формируем промпт с подстановкой языков messages = [ { "role": "system", "content": TRANSLATION_SYSTEM_PROMPT.format( source=source_name, target=target_name ), }, {"role": "user", "content": text}, ] response = _send_request( messages, max_tokens=160, temperature=0.2, # Низкая температура для точности перевода error_text="Произошла ошибка при переводе. Попробуйте ещё раз.", ) cleaned = response.strip() if not cleaned: return cleaned # Normalize to 2-3 variants separated by " / " parts = [] for chunk in re.split(r"(?:\s*/\s*|\n|;|\|)", cleaned): item = chunk.strip(" \t-•") if item: parts.append(item) if not parts: return cleaned parts = parts[:3] return " / ".join(parts)