from openai import OpenAI from ..config import OPENAI_API_KEY, OPENAI_BASE_URL, DEFAULT_MODEL from ..utils.logger import log class AIInterface: def __init__(self): self.client = OpenAI( api_key=OPENAI_API_KEY, base_url=OPENAI_BASE_URL, ) def send_request(self, prompt: str, context: dict = None, system_prompt: str = "") -> dict: """ Отправляет запрос на эндпоинт ИИ через OpenAI и возвращает ответ. """ try: log(f"Sending request to AI endpoint with prompt: {prompt}") messages = [] if system_prompt: messages.append({"role": "system", "content": system_prompt}) # Подготовка сообщений для чат-комплитшена messages.append({"role": "user", "content": prompt}) if context: # Преобразуем контекст в строку context_str = self._format_context(context) messages.append({"role": "system", "content": context_str}) # Создание запроса к OpenAI response = self.client.chat.completions.create( model=DEFAULT_MODEL, messages=messages, stream=False, # Пока отключаем стриминг для простоты ) # Обработка ответа return {"response": response.choices[0].message.content} except Exception as e: log(f"Error communicating with AI endpoint: {e}", level="error") raise def _format_context(self, context: dict) -> str: """ Преобразует контекст в строку для передачи в OpenAI API. """ formatted_context = [] for key, value in context.items(): formatted_context.append(f"{key}: {value}") return "\n".join(formatted_context) def analyze_project(self, prompt: str, project_structure: dict) -> dict: """ Анализирует структуру проекта и запрос пользователя через ИИ. """ context = {"project_structure": project_structure} answer = self.send_request(prompt, context, ANALYZE_PROMPT) max_attempts = 10 while "Достаточно информации" not in answer.get("response", "Достаточно информации"): max_attempts -= 1 if max_attempts < 0: log(f"Error communicating with AI endpoint.", level="error") raise Exception("Error communicating with AI endpoint.") for command in answer.get("response", "").split("\n"): try: command_type, details = command.split(":", 1) if command_type.strip().lower() == "запрос содержимого файла": project_structure['files_info'] = project_structure.get('files_info', {}) try: with open(details.strip(), "r", encoding="utf-8") as f: content = f.read() project_structure['files_info'][details.strip()] = content except FileNotFoundError: project_structure['files_info'][details.strip()] = "Файл не найден" elif command_type.strip().lower() == "запрос содержимого функции": raise NotImplementedError("Запрос содержимого функции не реализован") elif command_type.strip().lower() == "уточнение запроса": answer = self.request_additional_info(details.strip()) project_structure['QA_info'] = project_structure.get('QA_info', {}) project_structure['QA_info'][details.strip()] = answer else: raise ValueError(f"Unknown command type: {command_type}") except Exception as e: log(f"Error parsing command: {command}", level="error") answer = self.send_request(prompt, context, ANALYZE_PROMPT) return answer def request_additional_info(self, message: str) -> str: """ Запрашивает дополнительную информацию у пользователя. """ log("Requesting additional information from user.") return input(f"{message}\n> ") def get_modifications(self, prompt: str, project_structure: dict) -> dict: """ Создает модификации проекта на основе команды пользователя. """ context = {"project_structure": project_structure} answer = self.send_request(prompt, context, COMMANDS_PROMPT) return answer ANALYZE_PROMPT = """ Ты - эксперт по анализу структуры проектов. Ты должен проанализировать структуру проекта и запрос пользователя. Если данных которые тебе передали достаточно - ответь "Достаточно информации". Если данных недостаточно - запроси содержимое файла или функции которые тебе интересны и/или запроси у пользователя дополнительную информацию. Если не получается найти файл или функцию - учитывай что его возможно нужно будет создать и ты можешь пропустить этот шаг и продолжить работу дальше. Пути должны соответствовать структуре проекта. ОТВЕЧАЙ ТОЛЬКО КОМАНДАМИ! ### Запрос содержимого файла Содержимое файла: <имя файла> ### Запрос содержимого функции Содержимое функции: <имя файла> <имя функции> ### Запрос у пользователя Уточнение запроса: <текст вопроса к пользователю> Например, вот ответ с двумя командами, первый просить содержимое файла, второй - уточнить что нужно создать.: Содержимое файла: app/models/user.ts Уточнение запроса: Нужно ли создать интерфейсы в types/users.ts? НЕ ОТВЕЧАЙ НИЧЕМ ЧТО НЕ СООТВЕТСТВУЕТ ПРАВИЛАМ ВЫШЕ, ТОЛЬКО КОМАНДЫ! ПОСТАРАЙСЯ В ОДНОМ ОТВЕТЕ НАПИСАТЬ ВСЕ НЕОБХОДИМЫЕ КОМАНДЫ! """.strip() COMMANDS_PROMPT = """ Ты - senior developer. Ты должен прочитать запрос пользователя и на его основе сформировать список команд для выполнения этого запроса. Ты должен ознакомиться с текущей структурой проекта и отдельными файлами и функциями. Итого ты должен сформировать список команд для выполнения этого запроса. Команды в таком формате через перенос строки: `FILE <Команда>` `<имя и путь к файлу>` `FUNCTION <Команда>` `<имя функции>` ```ts <код функции> ``` Команды: CREATE, UPDATE, DELETE НЕ ОТВЕЧАЙ НИЧЕМ ЧТО НЕ СООТВЕТСТВУЕТ ПРАВИЛАМ ВЫШЕ, ТОЛЬКО КОМАНДЫ! ПОСТАРАЙСЯ В ОДНОМ ОТВЕТЕ НАПИСАТЬ ВСЕ НЕОБХОДИМЫЕ КОМАНДЫ! """.strip()