факит
This commit is contained in:
@@ -99,13 +99,17 @@ def run_workflow():
|
|||||||
ai_response = ai_interface.analyze_project(user_prompt, full_context)
|
ai_response = ai_interface.analyze_project(user_prompt, full_context)
|
||||||
log(f"AI response: {ai_response}")
|
log(f"AI response: {ai_response}")
|
||||||
|
|
||||||
# Шаг 5: Генерация плана модификации
|
ai_response = ai_interface.get_modifications(user_prompt, full_context)
|
||||||
command_generator = CommandGenerator()
|
log(f"AI response: {ai_response}")
|
||||||
modifications = ai_response.get("modifications", [])
|
|
||||||
modification_plan = command_generator.generate_commands(modifications)
|
|
||||||
|
# # Шаг 5: Генерация плана модификации
|
||||||
|
# command_generator = CommandGenerator()
|
||||||
|
# modifications = ai_response.get("modifications", [])
|
||||||
|
# modification_plan = command_generator.generate_commands(modifications)
|
||||||
|
|
||||||
with open("modification_plan.md", "w", encoding="utf-8") as f:
|
with open("modification_plan.md", "w", encoding="utf-8") as f:
|
||||||
f.write(modification_plan)
|
f.write(ai_response["response"])
|
||||||
|
|
||||||
log(f"Generated modification plan:\n{modification_plan}")
|
# log(f"Generated modification plan:\n{modification_plan}")
|
||||||
log("ReactDev workflow completed.")
|
log("ReactDev workflow completed.")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from openai import OpenAI
|
|||||||
from ..config import OPENAI_API_KEY, OPENAI_BASE_URL, DEFAULT_MODEL
|
from ..config import OPENAI_API_KEY, OPENAI_BASE_URL, DEFAULT_MODEL
|
||||||
from ..utils.logger import log
|
from ..utils.logger import log
|
||||||
|
|
||||||
|
|
||||||
class AIInterface:
|
class AIInterface:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.client = OpenAI(
|
self.client = OpenAI(
|
||||||
@@ -9,19 +10,24 @@ class AIInterface:
|
|||||||
base_url=OPENAI_BASE_URL,
|
base_url=OPENAI_BASE_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
def send_request(self, prompt: str, context: dict = None) -> dict:
|
def send_request(self, prompt: str, context: dict = None, system_prompt: str = "") -> dict:
|
||||||
"""
|
"""
|
||||||
Отправляет запрос на эндпоинт ИИ через OpenAI и возвращает ответ.
|
Отправляет запрос на эндпоинт ИИ через OpenAI и возвращает ответ.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
log(f"Sending request to AI endpoint with prompt: {prompt}")
|
log(f"Sending request to AI endpoint with prompt: {prompt}")
|
||||||
|
|
||||||
|
messages = []
|
||||||
|
|
||||||
|
if system_prompt:
|
||||||
|
messages.append({"role": "system", "content": system_prompt})
|
||||||
|
|
||||||
# Подготовка сообщений для чат-комплитшена
|
# Подготовка сообщений для чат-комплитшена
|
||||||
messages = [{"role": "user", "content": prompt}]
|
messages.append({"role": "user", "content": prompt})
|
||||||
# if context:
|
if context:
|
||||||
# # Преобразуем контекст в строку
|
# Преобразуем контекст в строку
|
||||||
# context_str = self._format_context(context)
|
context_str = self._format_context(context)
|
||||||
# messages.append({"role": "system", "content": context_str})
|
messages.append({"role": "system", "content": context_str})
|
||||||
|
|
||||||
# Создание запроса к OpenAI
|
# Создание запроса к OpenAI
|
||||||
response = self.client.chat.completions.create(
|
response = self.client.chat.completions.create(
|
||||||
@@ -46,17 +52,107 @@ class AIInterface:
|
|||||||
formatted_context.append(f"{key}: {value}")
|
formatted_context.append(f"{key}: {value}")
|
||||||
return "\n".join(formatted_context)
|
return "\n".join(formatted_context)
|
||||||
|
|
||||||
|
def analyze_project(self, prompt: str, project_structure: dict) -> dict:
|
||||||
def analyze_project(self, project_structure: str, prompt: str) -> dict:
|
|
||||||
"""
|
"""
|
||||||
Анализирует структуру проекта и запрос пользователя через ИИ.
|
Анализирует структуру проекта и запрос пользователя через ИИ.
|
||||||
"""
|
"""
|
||||||
context = {"project_structure": project_structure}
|
context = {"project_structure": project_structure}
|
||||||
return self.send_request(prompt, context)
|
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:
|
def request_additional_info(self, message: str) -> str:
|
||||||
"""
|
"""
|
||||||
Запрашивает дополнительную информацию у пользователя.
|
Запрашивает дополнительную информацию у пользователя.
|
||||||
"""
|
"""
|
||||||
log("Requesting additional information from user.")
|
log("Requesting additional information from user.")
|
||||||
return input(f"{message}\n> ")
|
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()
|
||||||
@@ -5,16 +5,19 @@ from pathspec import PathSpec
|
|||||||
from pathspec.patterns import GitWildMatchPattern
|
from pathspec.patterns import GitWildMatchPattern
|
||||||
|
|
||||||
from ..utils.logger import log
|
from ..utils.logger import log
|
||||||
|
from .file_analyzer import FileAnalyzer
|
||||||
|
|
||||||
|
|
||||||
class ProjectMap:
|
class ProjectMap:
|
||||||
def __init__(self, project_path: str):
|
def __init__(self, project_path: str):
|
||||||
self.project_path = project_path
|
self.project_path = project_path
|
||||||
self.gitignore_rules = self._load_gitignore()
|
self.gitignore_rules = self._load_gitignore()
|
||||||
|
self.file_analyzer = FileAnalyzer()
|
||||||
|
|
||||||
def generate_map(self) -> str:
|
def generate_map(self) -> str:
|
||||||
"""
|
"""
|
||||||
Создает карту структуры проекта, игнорируя файлы из .gitignore и директорию .git/.
|
Создает карту структуры проекта, игнорируя файлы из .gitignore и директорию .git/.
|
||||||
|
Добавляет информацию о функциях в файлы .ts, .tsx, .js, .jsx.
|
||||||
"""
|
"""
|
||||||
log("Generating project structure map...")
|
log("Generating project structure map...")
|
||||||
structure = self._traverse_directory(self.project_path)
|
structure = self._traverse_directory(self.project_path)
|
||||||
@@ -44,6 +47,15 @@ class ProjectMap:
|
|||||||
result += self._traverse_directory(full_path, level + 1)
|
result += self._traverse_directory(full_path, level + 1)
|
||||||
else:
|
else:
|
||||||
result += f"{indent}- {entry}\n"
|
result += f"{indent}- {entry}\n"
|
||||||
|
|
||||||
|
# Если это файл TypeScript/JavaScript, добавляем информацию о функциях
|
||||||
|
if entry.endswith((".ts", ".tsx", ".js", ".jsx")):
|
||||||
|
functions_info = self.file_analyzer.analyze_file(full_path)
|
||||||
|
if functions_info:
|
||||||
|
for func_name, details in functions_info.items():
|
||||||
|
params = ", ".join(details["params"])
|
||||||
|
return_type = details.get("return_type", "unknown")
|
||||||
|
result += f"{indent} FUNCTION {func_name}({params}): {return_type}\n"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def save_map(self, output_file: str = "react_app_map.txt"):
|
def save_map(self, output_file: str = "react_app_map.txt"):
|
||||||
@@ -78,4 +90,4 @@ class ProjectMap:
|
|||||||
spec = PathSpec.from_lines(GitWildMatchPattern, self.gitignore_rules)
|
spec = PathSpec.from_lines(GitWildMatchPattern, self.gitignore_rules)
|
||||||
|
|
||||||
# Проверяем, соответствует ли путь правилам
|
# Проверяем, соответствует ли путь правилам
|
||||||
return spec.match_file(path)
|
return spec.match_file(path)
|
||||||
6
StaticHelper/__init__.py
Normal file
6
StaticHelper/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from StaticHelper.ai_interface import AIInterface
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ai = AIInterface()
|
||||||
|
ai.work("")
|
||||||
|
|
||||||
197
StaticHelper/ai_interface.py
Normal file
197
StaticHelper/ai_interface.py
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
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 work(self, prompt):
|
||||||
|
with open("source/header.md", 'r', encoding='utf-8') as file:
|
||||||
|
header = file.read()
|
||||||
|
with open("source/docs.md", 'r', encoding='utf-8') as file:
|
||||||
|
docs = file.read()
|
||||||
|
with open("source/working_file.md", 'r', encoding='utf-8') as file:
|
||||||
|
wf = file.read()
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
log(f"Sending request to AI endpoint with prompt")
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{"role": "system", "content": header},
|
||||||
|
{"role": "user", "content": docs},
|
||||||
|
{"role": "user", "content": prompt+"\n\n"+wf}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Создание запроса к OpenAI
|
||||||
|
response = self.client.chat.completions.create(
|
||||||
|
model=DEFAULT_MODEL,
|
||||||
|
messages=messages,
|
||||||
|
stream=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
with open("source/old_input.md", 'w', encoding='utf-8') as file:
|
||||||
|
file.write(wf)
|
||||||
|
with open("source/working_file.md", 'w', encoding='utf-8') as file:
|
||||||
|
file.write(response.choices[0].message.content)
|
||||||
|
|
||||||
|
log(f"AI response received: {response.choices[0].message.content}")
|
||||||
|
# Обработка ответа
|
||||||
|
return {"response": response.choices[0].message.content}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log(f"Error communicating with AI endpoint: {e}", level="error")
|
||||||
|
raise
|
||||||
|
|
||||||
|
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()
|
||||||
4
StaticHelper/config.py
Normal file
4
StaticHelper/config.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
REACT_PROJECT_PATH = r'C:\Users\Mikan\PycharmProjects\tisbiProFi\frontend\react-source'
|
||||||
|
OPENAI_API_KEY = "secret" # Замените на ваш API-ключ
|
||||||
|
OPENAI_BASE_URL = "http://localhost:51337/v1" # Базовый URL для OpenAI
|
||||||
|
DEFAULT_MODEL = "gpt-4o-mini" # Модель по умолчанию
|
||||||
241
StaticHelper/source/docs.md
Normal file
241
StaticHelper/source/docs.md
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
# Требования к коду
|
||||||
|
|
||||||
|
Используется библиотека `react-router-dom` и `react=markdown`.
|
||||||
|
|
||||||
|
Все компоненты должны быть функциональными.
|
||||||
|
props должны быть сразу распакованы.
|
||||||
|
export без default
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export function ProfessionPage() {
|
||||||
|
const {professionId} = useParams<{ professionId: string }>();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Используйте современные практики React (например, хуки).
|
||||||
|
При создании новых компонентов добавьте комментарии.
|
||||||
|
|
||||||
|
Если для задачи необходимы новые типы, интерфейсы или вспомогательные компоненты - создавай их в соответвующих файлах.
|
||||||
|
|
||||||
|
# Документария
|
||||||
|
|
||||||
|
## Существующие типы
|
||||||
|
|
||||||
|
`types/api.ts`
|
||||||
|
```ts
|
||||||
|
export interface ApiResponse {
|
||||||
|
body?: object,
|
||||||
|
success: boolean
|
||||||
|
}
|
||||||
|
export interface PageResponse<T>{
|
||||||
|
count: number;
|
||||||
|
next?: string;
|
||||||
|
previous?: string;
|
||||||
|
results: T[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`types/common.ts`
|
||||||
|
```ts
|
||||||
|
export interface UniqueItem{
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageControl<T>{
|
||||||
|
items: T[] | null;
|
||||||
|
hasNext: boolean;
|
||||||
|
hasPrevious: boolean;
|
||||||
|
nextPage(): void;
|
||||||
|
previousPage(): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
`types/survey.ts`
|
||||||
|
```ts
|
||||||
|
import {UniqueItem} from "./common.ts";
|
||||||
|
|
||||||
|
export interface Survey extends UniqueItem{
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
interface Statement extends UniqueItem{
|
||||||
|
text: string;
|
||||||
|
score_variable: number;
|
||||||
|
}
|
||||||
|
export enum QuestionType{
|
||||||
|
QuestionPair,
|
||||||
|
AgreementQuestion
|
||||||
|
}
|
||||||
|
export interface Question extends UniqueItem{
|
||||||
|
questionType: QuestionType
|
||||||
|
}
|
||||||
|
export interface QuestionPair extends Question{
|
||||||
|
statements: Statement[]
|
||||||
|
}
|
||||||
|
export interface AgreementQuestion extends Question{
|
||||||
|
question: string;
|
||||||
|
score_variable: number;
|
||||||
|
}
|
||||||
|
export interface ScoreVariable {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
survey: number;
|
||||||
|
}
|
||||||
|
export interface Profession extends UniqueItem {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
export interface Specialty extends UniqueItem {
|
||||||
|
name: string;
|
||||||
|
institution: number;
|
||||||
|
link?: string;
|
||||||
|
}
|
||||||
|
export interface Institution extends UniqueItem {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
`types/users.ts`
|
||||||
|
```ts
|
||||||
|
import {UniqueItem} from "./common.ts";
|
||||||
|
|
||||||
|
export type UserContextType = {
|
||||||
|
user: Account | undefined;
|
||||||
|
login: (credentials: TokenObtainPair) => Promise<void>;
|
||||||
|
logout: () => void;
|
||||||
|
};
|
||||||
|
export interface ContactData{
|
||||||
|
first_name: string;
|
||||||
|
last_name?: string;
|
||||||
|
middle_name?: string;
|
||||||
|
phone?: string;
|
||||||
|
email?:string;
|
||||||
|
}
|
||||||
|
interface Role extends UniqueItem{
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
export interface Account extends UniqueItem, ContactData{
|
||||||
|
login: string;
|
||||||
|
role?: Role;
|
||||||
|
is_staff: boolean;
|
||||||
|
is_active: boolean;
|
||||||
|
}
|
||||||
|
export interface TokenObtainPair {
|
||||||
|
login: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
export interface TokenRefresh {
|
||||||
|
refresh: string;
|
||||||
|
}
|
||||||
|
export interface TokenVerify {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
export interface JwtTokenResponse {
|
||||||
|
access: string;
|
||||||
|
refresh: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Способы запросов
|
||||||
|
|
||||||
|
Вместо fetch запросы должны проходить при помощи специальных функций/функции
|
||||||
|
|
||||||
|
`API/common.ts`
|
||||||
|
```ts
|
||||||
|
async function getFetch(path: string) // Get method
|
||||||
|
async function postFetch(
|
||||||
|
path: string,
|
||||||
|
body: object | FormData
|
||||||
|
) // Post method
|
||||||
|
async function patchFetch(
|
||||||
|
path: string,
|
||||||
|
body: object | FormData
|
||||||
|
) // Patch method
|
||||||
|
async function deleteFetch(path: string) // Delete method
|
||||||
|
```
|
||||||
|
|
||||||
|
## Хуки
|
||||||
|
|
||||||
|
### Для загрузки данных
|
||||||
|
|
||||||
|
Для упрощения работы с api созданы следующие хуки
|
||||||
|
|
||||||
|
`API/hooks.ts`
|
||||||
|
```ts
|
||||||
|
function useDataPage<T>(application:string, endpoint: string, params:{[key:string]:string|number}={}, needLoad:boolean=true):PageControl<T>
|
||||||
|
function useData<T>(application:string, endpoint: string, id: string|number, needLoad:boolean=true): T|null|undefined
|
||||||
|
function useCachedData<T>(application: string, endpoint: string): (id: string | number, needLoad: boolean = true)=> T|undefined|null
|
||||||
|
```
|
||||||
|
- application - Название модуля
|
||||||
|
- endpoint - Название объекта над которым идёт работа
|
||||||
|
- id - id искомого объекта
|
||||||
|
- params - дополнительные параметры (например фильтрация)
|
||||||
|
- needLoad - указывает нужно ли загружать список/объект прямо сейчас
|
||||||
|
|
||||||
|
|
||||||
|
- null - загрузка еще не происходила
|
||||||
|
- undefined - не найдено
|
||||||
|
- T (объект)
|
||||||
|
|
||||||
|
### Для ленивой загрузки элементов
|
||||||
|
|
||||||
|
`components/LoadingData.tsx`
|
||||||
|
```tsx
|
||||||
|
function LoadingData({data, children}: {data: object|null|undefined, children: (ReactElement | null | undefined | boolean)[]})
|
||||||
|
```
|
||||||
|
|
||||||
|
Для загрузки отдельного элемента.
|
||||||
|
Если data
|
||||||
|
- null - Возвращает <div className={"loading"}>Идёт загрузка...</div>
|
||||||
|
- undefined - <Link className={"not-found"} to={"/"}>404 НЕ НАЙДЕНО</Link>
|
||||||
|
- иначе возвращает children
|
||||||
|
|
||||||
|
`components/LoadingList.tsx`
|
||||||
|
```tsx
|
||||||
|
function LoadingList({data, listElement}: {data: UniqueItem[]|null, listElement(e:UniqueItem, i:number):unknown})
|
||||||
|
```
|
||||||
|
|
||||||
|
Для загрузки списка. Если data не null - то возвращает элементы результаты `listElement`
|
||||||
|
|
||||||
|
### Примеры
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export function ProfessionPage() {
|
||||||
|
const { professionId } = useParams<{ professionId: string }>();
|
||||||
|
const profession = useData<Profession>('surveys', 'professions', professionId || 0);
|
||||||
|
const specialties = useDataPage<Specialty>('surveys', 'specialties', { profession: professionId || 0 }, !!profession);
|
||||||
|
|
||||||
|
const institutions = useCachedData<Institution>("surveys", "institutions");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<LoadingData data={profession}>
|
||||||
|
<h1>{profession?.name}</h1>
|
||||||
|
<div className="description">
|
||||||
|
<ReactMarkdown>{profession?.description || ''}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
<div className="specialties-block">
|
||||||
|
{specialties.items != null ? <h2>Где можно обучиться</h2>:null}
|
||||||
|
<LoadingList
|
||||||
|
data={specialties.items}
|
||||||
|
listElement={specialty => (
|
||||||
|
<SpecialtyBlock
|
||||||
|
key={specialty.id}
|
||||||
|
specialty={specialty as Specialty}
|
||||||
|
institution={institutions((specialty as Specialty).institution)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</LoadingData>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Авторизация
|
||||||
|
|
||||||
|
Везде доступен хук `useUser():UserContextType | undefined `
|
||||||
14
StaticHelper/source/header.md
Normal file
14
StaticHelper/source/header.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
Ты Senior разработчик React.
|
||||||
|
|
||||||
|
Твоя задача - писать код под задачи (отвечай ТОЛЬКО кодом).
|
||||||
|
|
||||||
|
Я могу кидать тебе просто код или информацию. Если я не прошу писать код - отвечай только "Понял".
|
||||||
|
Код, по возможности, только тот который необходимо внести в проект. Может быть работа сразу над несколькими файлами
|
||||||
|
|
||||||
|
Шаблон ответа кодом:
|
||||||
|
|
||||||
|
`Путь и название файла`
|
||||||
|
```ts
|
||||||
|
// Код который должен быть в этом файле
|
||||||
|
```
|
||||||
|
|
||||||
0
StaticHelper/source/old_input.md
Normal file
0
StaticHelper/source/old_input.md
Normal file
95
StaticHelper/source/working_file.md
Normal file
95
StaticHelper/source/working_file.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
`API/users.ts`
|
||||||
|
```ts
|
||||||
|
|
||||||
|
export async function login(data: TokenObtainPair): Promise<JwtTokenResponse | null>
|
||||||
|
|
||||||
|
export async function refreshToken(data: TokenRefresh): Promise<JwtTokenResponse | null>
|
||||||
|
|
||||||
|
export async function verifyToken(data: TokenVerify): Promise<boolean>
|
||||||
|
```
|
||||||
|
|
||||||
|
`utils/users/UserProvider.tsx`
|
||||||
|
```tsx
|
||||||
|
export function UserProvider({ children }: { children: ReactNode }) {
|
||||||
|
const [user, setUser] = useState<Account | undefined>(() => {
|
||||||
|
const storedUser = localStorage.getItem('user');
|
||||||
|
return storedUser ? JSON.parse(storedUser) : undefined;
|
||||||
|
});
|
||||||
|
const [tokens, setTokens] = useState<JwtTokenResponse | undefined>(() => {
|
||||||
|
const storedTokens = localStorage.getItem('tokens');
|
||||||
|
return storedTokens ? JSON.parse(storedTokens) : undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tokens?.access) {
|
||||||
|
localStorage.setItem('tokens', JSON.stringify(tokens));
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('tokens');
|
||||||
|
}
|
||||||
|
}, [tokens]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user) {
|
||||||
|
localStorage.setItem('user', JSON.stringify(user));
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('user');
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
const handleLogin = async (credentials: TokenObtainPair) => {
|
||||||
|
const tokens = await apiLogin(credentials);
|
||||||
|
if (tokens) {
|
||||||
|
setTokens(tokens);
|
||||||
|
// Здесь можно добавить запрос для получения данных пользователя
|
||||||
|
setUser({ id: 1, login: credentials.login, is_staff: false, is_active: true } as Account); // TODO
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
setUser(undefined);
|
||||||
|
setTokens(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateToken = async () => {
|
||||||
|
if (tokens?.access) {
|
||||||
|
const isValid = await verifyToken({ token: tokens.access });
|
||||||
|
if (!isValid && tokens.refresh) {
|
||||||
|
const newTokens = await refreshToken({ refresh: tokens.refresh });
|
||||||
|
if (newTokens) {
|
||||||
|
setTokens(newTokens);
|
||||||
|
} else {
|
||||||
|
handleLogout();
|
||||||
|
}
|
||||||
|
} else if (!isValid) {
|
||||||
|
handleLogout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
validateToken().then();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<UserContext.Provider value={{ user, login: handleLogin, logout: handleLogout }}>
|
||||||
|
{children}
|
||||||
|
</UserContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
В частности обрати внимание на эту функцию
|
||||||
|
```ts
|
||||||
|
const handleLogin = async (credentials: TokenObtainPair) => {
|
||||||
|
const tokens = await apiLogin(credentials);
|
||||||
|
if (tokens) {
|
||||||
|
setTokens(tokens);
|
||||||
|
// Здесь можно добавить запрос для получения данных пользователя
|
||||||
|
setUser({ id: 1, login: credentials.login, is_staff: false, is_active: true } as Account); // TODO
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Воспользуйся эндпоинтами /users/auth/users/me/ для получения id текущего пользователя
|
||||||
|
|
||||||
21
StaticHelper/utils/file_utils.py
Normal file
21
StaticHelper/utils/file_utils.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
def read_file(file_path: str) -> str:
|
||||||
|
"""
|
||||||
|
Читает содержимое файла.
|
||||||
|
"""
|
||||||
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
def write_file(file_path: str, content: str):
|
||||||
|
"""
|
||||||
|
Записывает содержимое в файл.
|
||||||
|
"""
|
||||||
|
with open(file_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
def create_directory(path: str):
|
||||||
|
"""
|
||||||
|
Создает директорию, если она не существует.
|
||||||
|
"""
|
||||||
|
os.makedirs(path, exist_ok=True)
|
||||||
13
StaticHelper/utils/logger.py
Normal file
13
StaticHelper/utils/logger.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
def log(message: str, level: str = "info"):
|
||||||
|
"""
|
||||||
|
Логирует сообщения в консоль.
|
||||||
|
"""
|
||||||
|
levels = {
|
||||||
|
"info": "[INFO]",
|
||||||
|
"warning": "[WARNING]",
|
||||||
|
"error": "[ERROR]",
|
||||||
|
}
|
||||||
|
prefix = levels.get(level, "[UNKNOWN]")
|
||||||
|
print(f"{prefix} {message}", file=sys.stderr if level == "error" else sys.stdout)
|
||||||
0
StaticHelper/utils/prompt_utils.py
Normal file
0
StaticHelper/utils/prompt_utils.py
Normal file
Reference in New Issue
Block a user